Context
This service must be able to use ANY provider:
- System Default (e.g., LogicSpike Twilio) for free users.
- Tenant BYOP (e.g., User's Twilio) for pro users.
- Custom Webhooks (e.g., User's internal API) for enterprise.
Decision
We will expose a Standard Plugin Interface and load implementations dynamically based on config.
Architecture
1. The Interface (The contract)
Every provider must implement this:
interface Provider {
id: string;
type: string;
send(message: MessagePayload): Promise<MessageResult>;
}2. The Config Table (The Router)
Database configs table stores:
tenant_id: "workspace_123"type: "twilio" | "smtp" | "webhook"credentials: Encrypted JSON ({ accountSid: "...", authToken: "..." })
3. The Factory (The Executer)
When a request arrives:
- Lookup config for
tenant_id. - Decrypt credentials.
- Instantiate the correct class:
new TwilioProvider(credentials). - Call
.send().
Trade-offs
- Latency: Creating a fresh API client per request adds overhead.
- Solution: Cache the client instance in-memory for hot tenants.
Status
Accepted.