Event Types
Each event type is a blueprint: how the call task is composed, what the agent knows, and what outcome schema comes back. Eight ship built-in; new ones are ~60 lines.
invoice.due
The overdue-invoice chase, done politely. The agent knows the amount, due date, days overdue, and last payment before dialing — so "I already paid last week" is handled with grace, not an argument.
| Outcome | Meaning | Auto-action |
|---|---|---|
payment_promised | A payment date was agreed (+ promise_date) | write.mark_promise |
claims_already_paid | Customer says it's paid | escalate.billing_review |
disputed | Charge contested | escalate.billing_review |
callback_requested | Wants a human | escalate.human_callback |
refused | Declines without a date | — (never redialed) |
no_answer | Unreachable | Redial up to 2× |
account.warning
An urgent-but-calm security notice. The agent never asks for passwords, PINs, or codes — and says so in its instructions. If the customer doesn't recognize the activity, the agent advises that a reset link was emailed and offers a security-team callback.
| Outcome | Meaning | Auto-action |
|---|---|---|
acknowledged | Notice received | — |
activity_confirmed_legitimate | "That was me" | — |
needs_human | Worried; wants the security team | escalate.human_callback |
no_answer | Unreachable | Redial up to 2× |
Payload: {"reason": "login from a new country", "detail": "A sign-in from Singapore was detected."}
promo.offer
A loyalty offer delivered by voice — warmer than SMS, and the agent answers questions in real time. Under a minute if they're not interested; a hard rule forbids pushing twice.
| Outcome | Meaning | Auto-action |
|---|---|---|
accepted | Took the offer | — |
declined | Said no | — (never redialed) |
callback_requested | Wants to decide later with a human | escalate.human_callback |
no_answer | Unreachable | Redial up to 2× |
Payload: {"offer": "20% off your next invoice", "expires": "end of this week"}
delivery.window
Confirm a delivery window — or collect one preferred alternative. The agent repeats the window back on confirmation and never leaves order details in a voicemail.
| Outcome | Meaning | Notes |
|---|---|---|
confirmed | Accepts the proposed window | Window repeated back |
reschedule_requested | Gives an alternative (+ preferred_window) | One alternative only |
callback_requested | Wants a human | — |
no_answer | Unreachable | Redial up to 2× |
Payload: {"window": "tomorrow, 2:00–4:00 PM", "order_ref": "ORD-8472"}
appointment.reminder
A warm reminder: confirm, reschedule, or cancel. The agent offers a callback for available times — it never invents times itself.
| Outcome | Meaning |
|---|---|
confirmed | Will attend |
reschedule_requested | Wants a new time |
cancelled | Cancels — accepted graciously |
no_answer | Unreachable — redial up to 2× |
Payload: {"what": "annual check-up", "when": "tomorrow at 10:00 AM"}
payment.failed
The declined-card notice done right: matter-of-fact, never accusatory, and card details are never discussed on the call — the payment link goes to the email on file.
| Outcome | Meaning |
|---|---|
will_update_payment | Will use the emailed link |
already_updated | Says it's fixed — account re-checked automatically |
callback_requested | Frustrated — billing callback offered |
no_answer | Unreachable — redial up to 2× |
Payload: {"amount": "the monthly charge", "reason": "the card was declined"}
subscription.expiring
One clear renewal offer with a loyalty discount. The offer is mentioned once; if they still decline, the call ends warmly — never a second push.
| Outcome | Meaning |
|---|---|
renewed | Accepts the renewal — details emailed |
declined | Declines — terminal, never redialed |
callback_requested | Wants to talk to the team |
no_answer | Unreachable — redial up to 2× |
Payload: {"when": "in 7 days", "offer": "a 10% loyalty discount on renewal"}
feedback.request
A two-minute feedback call: one 1–5 satisfaction rating and one open comment. Unhappy customers get a sincere apology and a callback offer.
| Outcome | Meaning |
|---|---|
provided | Gave feedback (+ rating 1-5, comment) |
busy_callback_requested | Busy — asked to call back |
no_answer | Unreachable — redial up to 2× |
Payload: {"topic": "their recent experience with the service"}
Adding your own
A blueprint lives in internal/events/router.go and has exactly two required parts:
"delivery.window": {
Compose: func(c, e, s) (string, error) {
// build the call task with prefetched business data
},
ResultSchema: map[string]any{
// JSON Schema — CALL-E extracts this from call evidence
},
},
Intake, routing, scheduling, retries, windows, persistence, outcome actions, dashboard — all of it is generic. The blueprint is the only project-specific code.
GoalID + Variables) for stricter governance. See Architecture → Goals.