Here is what it takes to handle inbound communications across five channels without a Channel Abstraction Layer:
Voice (PSTN): WebSocket audio stream, real-time speech-to-text, DTMF input handling, call state machine (ringing, connected, on hold, terminated), SIP signaling, WebRTC ICE negotiation.
SMS: GSM 03.40 PDU decoding, multi-part message concatenation, character set detection, STOP/HELP/UNSTOP keyword interception, delivery receipt parsing.
Email: MIME multipart parsing, HTML-to-text extraction, attachment handling, thread detection via References/In-Reply-To headers, SPF/DKIM verification.
WhatsApp: Meta webhook parsing, session window management (24-hour constraint), template vs. free-text message classification, media URL resolution.
iMessage: Apple Business Chat proprietary format, rich card parsing, action button handling, Apple Business Register auth.
To handle all five, you'd build five separate integrations with five different authentication models, five different data formats, five different error handling patterns, and five different compliance requirements.
Then you'd have to normalize all of them into something your agent can actually reason about.
What the CAL does
The Channel Abstraction Layer does that normalization — once, at the infrastructure level, for every channel — and delivers every inbound communication to your agent as a single, unified format:
{
"acp_version": "1.0",
"message_id": "msg_01h9x7y3z4",
"session_id": "sess_lead_982",
"from": {
"acn": "acn://getdial.ai/genway/sales-agent-7f3a",
"entity_type": "agent",
"signature": "ed25519:..."
},
"to": {
"address": "+14155559876",
"preferred_channel": "auto"
},
"content": {
"type": "text",
"body": "I have a question about the enterprise pricing."
},
"channel_meta": {
"resolved_channel": "sms",
"delivery_status": "delivered"
},
"context": {
"thread_id": "thread_lead_982",
"turn_index": 4,
"prior_turns_summary": "Lead confirmed interest in enterprise plan after call on May 20."
},
"policy": {
"consent_verified": true,
"tcpa_consent_timestamp": "2026-05-20T10:14:22Z"
}
}Regardless of whether this message came in as a phone call, an SMS, an email, or a WhatsApp — your agent sees this format. The CAL did the translation.
For a voice call, that means the CAL ran speech-to-text and put the transcript in content.body. Your agent doesn't know or care that it was audio. For a multi-part SMS, the CAL concatenated the segments. For a WhatsApp message, the CAL resolved the media URL and extracted the text.
Outbound routing
The same abstraction works in reverse. When your agent sends an ACES message:
client.send(
from_acn="acn://getdial.ai/genway/sales-agent-prod",
to="+14155559876",
content={
"type": "voice",
"body": "Hi, following up on your enterprise trial. Do you have time for a quick call?",
"tts_hint": "professional"
},
channel="auto",
session_id="sess_lead_982",
routing_hints={
"urgency": "medium",
"recipient_tz": "America/New_York",
"local_time": "14:30"
}
)The CAL decides: 14:30 Eastern, medium urgency — appropriate time for a call. Message length is short. Prior channel was SMS. Route to voice.
If it were 23:45, the CAL would route to SMS instead, because calling someone at midnight is not appropriate. The agent specified its intent ("I want to reach out about the trial"). The infrastructure made the channel decision.
If the call fails to connect, the CAL falls back to the fallback_channels policy: SMS first, then email. The agent doesn't handle this logic. The infrastructure does.
The translation table
| Channel | What the CAL handles |
|---|---|
| Voice (PSTN) | Agent sends text; CAL synthesizes speech. Inbound: CAL transcribes, sends text to agent. DTMF captured as structured input. |
| SMS (A2P) | CAL manages 10DLC registration, character segmentation, STOP/HELP interception, delivery receipts. |
| CAL manages DKIM signing, sending infrastructure, bounce management, unsubscribe injection. | |
| CAL manages session windows, template compliance, media handling. | |
| iMessage | CAL manages Apple Business Register auth, rich card rendering. |
In every case: the agent sends intent, the CAL handles the channel.
Why this matters for multi-channel workflows
Multi-channel workflows fail today not because the logic is hard, but because the channel complexity overwhelms the logic.
A workflow that says "call the lead, if no answer send SMS, if no reply send email" should be three lines of logic. With channel abstraction, it is:
client.send(
to=lead.phone,
content={"type": "voice", "body": "..."},
fallback_channels=["sms", "email"],
session_id=lead.session_id
)Without channel abstraction, it's three separate integrations, three authentication systems, three error handling patterns, and a state machine to track where you are in the fallback sequence.
The CAL moves channel complexity from every agent implementation to one infrastructure layer. That's where complexity belongs.