Connect by JBRH Open Connect

Outbound phone call, end to end

An outbound call starts as a reason — a follow-up falling due, an instruction, a person pressing dial — and then passes the same gates a message would: memory blocks, consent, autonomy, hours, budget. Only then is a dial handed to the provider. Everything after that is timing and evidence: did the leg join, did it ring, did anyone speak, and what does the record say when it did not.

Status
Available What this means
Audience
both
Channels
phone
In the app
#/calls, #/follow-ups
Last verified
Product version
6.3.2

The chain, when Connect is the one calling#

  1. Trigger — a due follow-up, a standing instruction, a supervisor's decision, or a person dialling.
  2. User or external event — the reason is already a record; nothing external has happened yet.
  3. Authentication and workspace resolution — the placer is identified and the workspace is entered before any line setting is read.
  4. Ingest — the request names the contact, the line and the purpose.
  5. Canonical record — the intent to call exists before the dial does, so a failure has somewhere to be written.
  6. Reasoning — the purpose selects the persona and the opening; the brief is rendered once.
  7. Knowledge, memory and rules — contact facts, standing instructions and hard rules go into the brief under fixed character budgets, and voice_region.plan adds the language rule for this person.
  8. Autonomy and approval — memory blocks, consent, suppression, autonomy mode and budget_refusal all gate the dial.
  9. Action through a provider — comms_providers.dispatch_call places the leg with a placer, exactly as a carrier call would be.
  10. Result — answered, no_answer (it rang, nobody picked up), or not_reached (it never rang).
  11. Relationship, timeline and memory — the outcome lands on the person, whether or not there was a conversation.
  12. Audit, usage and Needs You — the cost is booked, the attempt is on the timeline, and a call worth a person's attention is queued.

The one rule worth memorising: an outbound call from the realtime engine goes through the same dispatch as a carrier call, with a placer recorded. That is deliberate. It means switching engines cannot quietly bypass consent, autonomy or budget, because there is only one door.

An outbound call knows more about the person than an inbound one usually does, and the language rule uses it. If the record carries a preferred_language — saved on an earlier call that was allowed to use memory, latest value winning — Connect speaks it from its first reply after the opening line, and only a whole sentence in another language moves it. If it carries a city or state instead, that language is *likely*, not assumed: the call opens in the line's language and follows the person into the regional one as soon as they use two or three words of it. Where the number is all there is, the dialling code gives a country, and an Indian landline's STD code a city — a mobile gives neither, because portability made the series meaningless.

The gates, in the order they refuse#

GateWhat you seeWhat changesWhat can fail
Memory block or do-not-contactThe call is not attempted and the reason is shownThe refusal is recorded against the contactA block set on one identity while the number belongs to another row
Consent and suppressionSameNothing is dialledA number with two contact rows — identify_caller resolves to the oldest
Autonomy modeEither it goes, or it waits in Needs YouAn approval record, if a person must say yesA mode set at the wrong scope; the narrowest scope wins
Line hours and permissionRefused with the line's own reasonNothing dialledA line without outbound permission on channel_routes
budget_refusalRefused before the dialNothing dialledThe workspace's cap already spent by earlier calls
ai_quota breakerScheduled work is skippedThe breaker opens on a 429 or a revoked keyA caller already on the line is still attempted — the breaker is not a hard stop for live conversation
ring admissionnot_ready, and the drain retriesNo Call row yetNo worker checked in for 90 s, or a malformed or Vertex-path model id (bad_model)

The dial itself, and what a non-answer means#

Once a dial is out, the interesting states are the ones that are not a conversation. Connect gives them separate words because they need separate responses, and because a lead invented out of a call that never connected is worse than no lead.

no_answer
It rang and nobody picked up. Try again later; the contact is real and reachable.
not_reached
It never rang. The dial, the dispatch or the worker failed. This is an operational problem, not a sales one.
Dial timeout
A carrier answer of 408, 503 or 504 is dialled once more, and once only.
Leg answered, never joined
The carrier says the leg was answered but it never appears in the room. It is written off after DIAL_JOIN_TIMEOUT_S rather than waited on indefinitely.

_capture_lead creates nothing from either of the two non-conversations. That single rule is what keeps a week of failed dials from turning into a pipeline of people who never spoke to anyone.

What is written when nobody speaks#

Every attempt leaves a record, which is the point of opening the intent before the dial. The row carries the outcome word, the attribution for the ending, and the cost — a call that reports no tokens is still charged from its own duration at the published per-minute rate, never at zero. Stale sessions are swept to the configured cap from the engine tick as well as the worker heartbeat, because a browser call has no worker and one such call once sat active for the better part of a day.

If the call was placed to keep a promise, the follow-up is not closed by the attempt alone. A promise with nothing behind it has its own finding, promised_unbooked, and it exists so that "I called them" and "they know" stay different facts.

Questions#

Does an outbound call from the realtime engine skip any of the checks?

No. It goes through comms_providers.dispatch_call with a placer, so memory blocks, consent, autonomy and budget gate it exactly as they gate a carrier call. There is one door, and both engines use it.

Why was a call refused with not_ready when the Phone screen looked fine?

ring refuses before any row exists when no worker has checked in for 90 seconds, or when every live worker is over the busy-load threshold. The refusal is deliberately retryable so a deploy drain picks the call up again rather than losing it.

Can Connect send a text message instead when a call is not answered?

Not on the live carrier, which carries no SMS. The Phone screen says so rather than presenting a thread that cannot send. A follow-up on another channel, or a task for a person, is the honest alternative.