Your system fires a webhook. callhook places an intelligent phone call via CALL-E, handles the conversation with full customer context, and POSTs a structured outcome back.
$ curl -fsSL https://callhook.github.io/install.sh | bash
$ ./callhook/callhook_linux_amd64/callhook
→ the war room opens at http://localhost:8080
{outcome, promise_date, actions, transcript, confidence}
The customer's plan, invoice, and payment history are prefetched and baked into the call — the agent never wastes seconds asking for account details.
When the customer says "I already paid," the agent acknowledges and flags the account for review instead of arguing. CALL-E's voice brain handles it.
Results arrive as JSON-Schema-validated objects — payment_promised, promise_date, confidence — never free text you have to parse.
Unambiguous outcomes write to your business store; uncertain ones escalate to a human. Every action is audit-logged. Deterministic code, not model vibes.
| Guarantee | How |
|---|---|
| Never double-dial | Per-attempt idempotency keys + event dedup persisted across restarts |
| Crash-safe | Append-only JSONL journal; sessions, armed retries and schedules replay on boot |
| Polite hours | Calls deferred to 9:00–20:00 recipient-local, weekdays, per-region timezone |
| Courtesy retries | no_answer redials up to 2×; refusals and blocked numbers never redialed |
| Scheduled calls | not_before on any event parks it until the requested time |
| Not forgeable | Bearer-token intake + shared-secret webhook (set both on a public tunnel) |
Fire one webhook per call — or declare a goal and let callhook run the waves: "collect 5 payment promises from the overdue list." Waves launch, unanswered customers are requeued, and the campaign stops the moment the goal is met — everyone not yet called is skipped, the budget stays unspent. See campaigns →
Stripe, Shopify, n8n, Zapier, cron + curl — one webhook endpoint with idempotency built in. Structured outcomes POST back; AI agents drive it over MCP; your CRM plugs in via one interface. Integrations →
# start callhook (dry-run: no API key, no real calls, no balance)
$ go run ./cmd/callhook
callhook listening on :8080 (dashboard: http://localhost:8080/)
# any business system:
$ curl -X POST localhost:8080/api/events -H 'Content-Type: application/json' -d '{
"id": "evt_001",
"type": "invoice.due",
"customer_id": "cus_1002",
"callback_url": "https://your-app.example.com/hooks/callhook"
}'
# → {"status":"call_placed","call_id":"call_..."}
# → outcome lands on your callback URL when the call finishes