Recipes← all recipesView on GitHub

Write call dispositions from data your handlers recorded

AI Agentscall disposition from handler data

Build call dispositions only from qualification fields recorded by tool handlers, not from transcripts or model summaries.

Also called CRM logging after a call, call disposition, lead qualification notes

governancepost-prompt

The claim

Each tool handler validates one fact and writes it with a set_global_data action. At the end of the call the platform POSTs to post_prompt_url with global_data in the body. on_summary builds the record from that POST. The call and caller identifiers come from the envelope and the qualification fields from global_data. on_summary keeps the model’s summary as a note that nothing else reads, and leaves the transcript in the same POST unread.

Why it holds

The post-prompt callback documents the body. The platform includes global_data and call_log when action is post_conversation, and post_prompt_data carries the model’s answer as raw and parsed.

How it works

Handlers accept or refuse, and write only what they accepted:

def record_budget(self, args, raw_data):
    amount = args.get("amount")
    if type(amount) is not int or amount <= 0:  # bool is an int in Python
        return FunctionResult("INVALID: ask for the budget as a number of dollars.")
    r = FunctionResult(f"Budget recorded: {amount} dollars.")
    r.add_action("set_global_data", {"budget": amount})
    return r

on_summary reads global_data from the POST body, not the summary:

def on_summary(self, summary, raw_data=None):
    data = raw_data.get("global_data") or {}
    budget, can_sign = data.get("budget"), data.get("can_sign")
    return {"budget": budget, "can_sign": can_sign,
            "qualified": bool(budget and budget >= MIN_BUDGET and can_sign is True),
            "complete": all(k in data for k in ("budget", "timeline_weeks", "can_sign")),
            "model_note": summary}

Code decides qualified from handler-written fields. complete says whether all three expected keys are present, rather than filling a gap from prose. The wire key handlers emit is set_global_data. set_post_prompt puts the post_prompt text in the document and the SDK adds post_prompt_url itself.

The record lands in an in-memory list, DISPOSITIONS, which a restart clears. Your version posts it to the CRM in the same place.

Limitations

post_prompt_url is a URL the SDK builds from its own host, carrying the basic-auth credentials and a per-call token. The platform must reach it, which on a laptop means the same tunnel as the webhook.

The record keeps the model’s summary as model_note. Delete that line if your CRM must never hold model prose.

What to change first

In on_summary, replace data.get("budget") with a value read from the summary and run the verifier. The contradiction test fails: the record now says two million, which is the failure this recipe exists to prevent.