Advanced Configuration

The basic patterns — register a connector, build a message, send it — cover the common cases. Production deployments introduce additional concerns: keeping credentials secure, monitoring connector health, understanding performance characteristics, and testing thoroughly without sending real messages.

This section covers these production patterns. Each pattern is independent — apply the ones that match your deployment context.

Security

Credential management

Never store secrets in source code. Use environment variables, user secrets (development), or a vault (production):

// appsettings.json — use placeholders, not real values
{
  "Twilio": {
    "AccountSid": "",
    "AuthToken": ""
  }
}

// Environment variables override at runtime
export Twilio__AccountSid="AC..."
export Twilio__AuthToken="..."

Sensitive parameter redaction

Mark schema parameters as IsSensitive — the framework redacts their values in logs:

When logging, sensitive parameter values appear as "***" instead of the actual value.

Webhook signature validation

Inbound webhooks from providers include cryptographic signatures. Always validate them:

  • Twilio: validate X-Twilio-Signature header using your auth token

  • Telegram: set SecretToken and validate X-Telegram-Bot-Api-Secret-Token

  • Facebook: validate X-Hub-Signature-256 using your app secret

  • SendGrid: validate X-Twilio-Email-Event-Webhook-Signature

Named connector isolation

Use named connectors to isolate different connector instances:

Runtime schema selection

Health checks

Built-in health check

Manual health verification

Health check pattern

Observability

Structured logging

ChannelConnectorBase automatically creates structured logging scopes:

Metrics to track

For production monitoring, track per-connector metrics:

Metric
Source
What it detects

Send attempts

Count before SendMessageAsync call

Volume trends

Send success rate

OperationResult.IsSuccess()

Provider degradation

Send latency

Stopwatch around SendMessageAsync

Provider performance

Error codes

OperationResult.Error.Code

Failure type distribution

Connection state

Connector.State

Connectivity issues

Retry policies

Connector send operations can be configured with automatic retry for transient failures using a policy-based approach. See Retry Policies for full documentation.

Performance

Bulk sending

Prefer SendBatchAsync over individual SendMessageAsync calls when sending multiple messages:

Concurrency

For high-volume sends, use bounded concurrency rather than unbounded parallelism:

Schema caching

If you build schemas dynamically, cache them:

Connector disposal

ChannelConnectorBase implements IDisposable and IAsyncDisposable. When registering via DI (AddConnector<T>()), the container manages disposal. For direct instantiation:

Schema versioning

As connectors evolve, their schemas change — new capabilities are added, parameters become obsolete, message property constraints are tightened. Schema versioning using semantic versioning helps track these changes and prevents incompatible schemas from being used interchangeably. The logical identity returned by GetLogicalIdentity() includes the version, and IsCompatibleWith() returns false for schemas with different versions.

Use semantic versioning in your schemas:

GetLogicalIdentity() returns "Twilio/SMS/1.0.0". Schemas with different versions are not compatible (IsCompatibleWith returns false), which prevents accidentally mixing different schema versions in runtime operations.

Testing patterns

The layered design of the framework makes testing straightforward: you can test validation rules without a connector, test connector logic without a provider, and test provider integration with controlled sandbox environments.

Unit testing validation rules

The schema and validation logic live entirely in memory with no I/O — test them with plain xUnit facts.

Mocking connectors

When your service depends on IChannelConnector, you can replace it with a mock for unit tests. This lets you test your business logic — retry policies, fallback routing, logging — without any provider dependency.

Integration testing with real credentials

For end-to-end tests that verify the connector actually communicates with the provider, use sandbox or test credentials that do not produce real side effects:

  • Twilio: use Test Credentials from the Twilio Console (they accept any To number and return mock responses)

  • SendGrid: enable SandboxMode to prevent actual email delivery

  • Firebase: enable DryRun to validate without sending to devices

  • Telegram: create a test bot with BotFather

  • Facebook: use a test page and test users from Facebook Developer Console

When building a custom connector, you want to test the message translation logic without calling the provider's API. A common technique is to expose the core methods through a testable subclass that bypasses initialization:

Test your connector's core logic without hitting the provider by subclassing:

Last updated