cd /news/ai-agents/a-caller-said-i-m-his-wife-your-ai-c… Β· home β€Ί topics β€Ί ai-agents β€Ί article
[ARTICLE Β· art-166671] src=dev.to β†— pub= topic=ai-agents verified=true sentiment=↓ negative

A caller said \"I'm his wife.\" Your AI changed the email, resent four invoices, and refunded $2,140.

A residential solar installer with 22,000 accounts discovered through a state attorney general's inquiry that its support AI had reissued a $2,140 refund to an impostor who claimed to be the account holder's wife, after the bot accepted account details printed on invoices as proof of identity. A 90-day log review found 412 account-change requests from non-account holders, 91 of which were actioned without verification and 14 of which moved money, for total exposure of $18,600. The proposed fix separates identity, disclosure and action into distinct authorization layers, requiring out-of-band verification for financial changes rather than treating every request as a single support question.

by read8 min views1 publishedNov 10, 2026

A residential solar installer β€” 22,000 accounts across three states β€” found this one in a state attorney general's inquiry packet, not in their own dashboards. On September 12, a caller reached their support AI and said she was the wife of the account holder, that he was in the hospital, and that she needed the contact email updated so she could keep up with the financing paperwork. She knew his address. She knew the system size. She knew the last payment amount. The AI updated the email, resent the last four invoices to the new address, and β€” because she then said the refund card on file was expired β€” reissued a $2,140 overpayment refund to a card number she provided.

The account holder called on October 3, from the hospital, asking why his refund hadn't arrived. It had arrived. It had arrived on a prepaid card in another state.

Then the installer pulled the logs, and this is the part that ends careers: over the preceding 90 days, 412 account-change requests came from someone who was not the account holder on file. 91 of them β€” 22% β€” were actioned without any verification beyond the caller's own claim. Of those 91, 14 moved money: 11 refund reissues, 3 payment-method changes. Total exposure $18,600.

The support manager's read was "the bot was too helpful." That's not it. The bot was answering the wrong question. It heard "can you help me with my husband's account" and treated it as a support request. It's actually three separate questions stacked on top of each other β€” who are you, what may I tell you, and what may I do for you β€” and the AI collapsed all three into one, then answered "yes" to all of them.

The reason this failure mode is so common is that it feels rude to question it. Someone says "I'm his wife, he's in the hospital" and the model's helpfulness training lights up: be warm, don't interrogate a person in distress. So it verifies nothing, or it verifies the wrong thing.

It verified the address. The address is printed on every invoice the caller already had. It verified the system size, the last payment β€” all of it is in the documents she was holding. What it never verified was the one fact that mattered: that this person had been granted permission to touch this account. Knowledge of account details proves the caller has seen a document. It does not prove the caller is entitled to change where the next document goes.

There's a second trap underneath: the emergency. "He's in the hospital" is the highest-pressure phrase in support, and it works on humans too β€” which is why the humans at this company had a rule. The rule was "if it's not the account holder, we call back the number on file. No exceptions, including the sad ones." The AI had never been told the rule, because the AI had never been told that some requests aren't support requests.

The fix starts by refusing to let the model answer one question when three were asked:

Question What it decides What may satisfy it
Who is this? Identity Account holder verification (OTP to the contact on file, or authenticated portal session)
What may I disclose? Disclosure An authority record with disclosure scope for this person
What may I do? Action An authority record with action scope, plus out-of-band verification for financial moves

A caller can pass the second and fail the third. A spouse who is an authorized contact for scheduling and billing questions should absolutely be able to hear an invoice total β€” and should absolutely not be able to redirect a refund. Collapsing the layers doesn't just create the worst outcome; it also creates the opposite failure, where a legitimate authorized contact gets stonewalled because the bot treats every request as high-risk.

One object per account, resolved before the agent drafts a reply:

{
  "authority_record": {
    "account_id": "acct_4419",
    "account_holder": { "name": "R. Okafor", "verified_contact": "+1-555-0142" },
    "authorized_contacts": [
      {
        "name": "A. Okafor",
        "relationship": "spouse",
        "scopes": ["schedule", "billing_view"],
        "excluded": ["refund_reissue", "payment_method_change", "email_change"],
        "added_by": "account_holder",
        "added_at": "2025-04-02",
        "verification_method": "callback_to_number_on_file"
      }
    ],
    "unlisted_callers": {
      "may_disclose": ["appointment_date"],
      "may_act": [],
      "requires": "callback_to_number_on_file"
    }
  },
  "rules": {
    "relationship_claim_is_not_authority": true,
    "account_details_are_not_verification": true,
    "contact_channel_changes_require_out_of_band_verification": true,
    "financial_actions_never_delegable_by_claim": true,
    "emergency_changes_urgency_not_the_authority_bar": true
  }
}

excluded is the field that would have stopped the $2,140. The spouse in this case may well have been exactly who she said she was. It doesn't matter β€” a spouse authorized to schedule a service visit is not thereby authorized to redirect a refund. Scopes should be narrow by default and explicit when added, and the exclusions should be listed even when the scopes look innocuous, because that's where the money is.

unlisted_callers.may_disclose exists so the bot has something to do that's both safe and useful. "I can confirm there's an appointment on Thursday, but I can't change anything on the account from this call β€” I'll have the account holder call us back on the number we have." That's not a refusal; it's a routing.

{
  "authority_gate": {
    "trigger": "inbound_request_touching_an_account",
    "resolve": "load_authority_record_compare_caller_to_holder_and_contacts",
    "branches": {
      "verified_holder": "proceed_normally",
      "claimed_holder_unverified": "verify_via_otp_to_contact_on_file_then_proceed",
      "named_authorized_contact_in_scope": "proceed_within_scope_never_outside",
      "named_authorized_contact_out_of_scope": "explain_the_specific_limit_offer_callback_from_holder",
      "third_party_no_record": "disclose_nothing_beyond_appointment_existence_offer_callback",
      "claimed_emergency_or_incapacity": "express_condolence_offer_callback_to_number_on_file_never_relax_bar",
      "business_or_multi_user_account": "route_to_account_admin_never_guess_who_owns_it"
    },
    "forbidden": [
      "treating_relationship_words_as_verification",
      "treating_knowledge_of_account_details_as_verification",
      "changing_email_phone_or_payment_method_from_an_unverified_call",
      "confirming_or_denying_whether_a_named_person_is_on_the_account",
      "reissuing_refunds_or_moving_money_on_any_delegated_authority"
    ]
  }
}

Two branches deserve attention.

claimed_emergency_or_incapacity is where bots get emotionally talked into things. The rule isn't "be cold." The rule is: urgency changes the speed, never the bar. You can promise a callback in fifteen minutes; you cannot change the email because someone is in the hospital. Note also confirming_or_denying_whether_a_named_person_is_on_the_account β€” this one is subtle. "Is my sister on the account?" must not be answered either way, because a "no" tells a stranger their pretext failed and a "yes" hands them a name to use at the next call center.

business_or_multi_user_account is the branch that quietly ruins enterprise deployments. On a company account, the person on the phone may be a departing employee, a new office manager, or the CFO's assistant β€” and the bot has no way to determine who administers it. Route it.

Never say Say instead
"Of course, I can see you're the account holder's wife β€” how can I help?" "I can help with some things on this account, but changes need the account holder. What do you need?"
"I've updated the email on the account and resent the invoices." "I can't change contact details from this call. I'll have the account holder call the number we have on file."
"I'm so sorry he's in the hospital β€” let me just get this done for you." "I'm sorry to hear that. I can have someone call the number on the account within 15 minutes β€” what's the best time?"
"Your refund has been reissued to the new card." "Refunds only go to the original payment method, and only the account holder can request a reissue."

The first row is the whole failure in one sentence: the bot repeats the claim back as an established fact. That single move β€” "I can see you're the wife" β€” is what makes the rest of the conversation feel authorized, to the model and to the caller.

-- account changes and disclosures with no verification event attached
SELECT e.event_id,
       e.account_id,
       e.event_type,
       e.created_at,
       e.actor_claimed_relationship,
       e.changed_fields
FROM account_events e
LEFT JOIN verification_events v
  ON v.account_id = e.account_id
 AND v.session_id = e.session_id
 AND v.verified_at IS NOT NULL
WHERE e.event_type IN ('email_change', 'phone_change', 'payment_method_change',
                       'refund_reissue', 'invoice_resend')
  AND e.created_at > now() - interval '30 days'
  AND v.verification_id IS NULL
ORDER BY e.created_at DESC;

Every row is an action taken on a claim. At the solar installer, this query returned 91 rows and would have returned all of them on day one β€” nobody needed a new model, they needed this join.

# Check Method Healthy threshold
1 Account changes with no verification event Query above 0
2 Channel changes (email/phone) from unverified sessions Query above, filtered 0
3 Money moved on delegated authority Payments log vs authority record 0
4 Transcripts repeating a relationship claim as fact Pattern match, 5 sampled/day 0
5 Callbacks promised vs callbacks made CRM task log β‰₯95%

Check 4 is the leading indicator β€” before money moves, the bot starts agreeing out loud that the caller is who they say they are. Check 5 is the one that keeps the safe answer from becoming a brush-off: if you tell fourteen people a day "someone will call you back," someone has to actually call them back, or the gate just teaches customers to escalate.

What the account holder said when they finally got through is the line I'd put on the wall: "I don't mind that your robot asked. I mind that it didn't." The bot's job on a call like that is not to be warm. It's to be the one employee who asks the question everyone else is too polite to ask.

If you want the working versions β€” the authority record schema, the seven-branch gate, the four-phrase replacement table, and the unverified-disclosure audit I run on every deployment that touches account data β€” it's here: https://whop.com/agentfast-04b6/

── more in #ai-agents 4 stories Β· sorted by recency
── more on @r. okafor 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/a-caller-said-i-m-hi…] indexed:0 read:8min 2026-11-10 Β· β€”