What Happened When I Built a Donor Nurture Agent in Agentforce.
- Martin Farrell
- 7 minutes ago
- 8 min read
I built a donor nurture agent in Agentforce Builder, in a Trailhead Playground dev org, because I wanted to test whether an agent scaffolded from a single prompt could be created quickly for a nonprofit organization.
The agent never worked. Partway through the build, the Problems panel began shifting on its own, duplicating and reclassifying errors without any new edits being made. Underneath that instability sat one error I could never resolve, no matter what I tried, and it permanently blocked me from testing the agent. This article documents that build from start to finish: what I asked for, what the tool actually built, where it went wrong, and exactly what I tried in order to fix it.
Setting up the org
I provisioned the org through the "Marketing Initial Setup" flow in Salesforce Go. Data Cloud was enabled, the Marketing data kits were installed, a CRM Connector was created, a default email channel was added, and a Data Space was selected. The guided setup completed all of this automatically.
I clicked Generate on the Identity Resolution ruleset for Unified Individual, because the agent's later donor-profile analysis depends on a resolved donor record.

Figure 1: Identity Resolution success, UnifiedIndividual__dlm mapped, Success badge.
I confirmed Agentforce was activated and that Agentforce Builder and Agentforce Studio were reachable, because the agent couldn't be built until both were live.
Building the agent
I sent the full build prompt in two parts, an initial capabilities/guardrails message, then a follow-up with specific DMOs, compliance fields, and output object, because the builder's clarifying-question flow takes scope in stages.
Agentforce Panel:
Hello! How can I help you with your Agent?
Build an autonomous marketing assistant that analyzes donor profiles in Data Cloud to generate personalized email nurture content and CTAs.
Key Capabilities (Subagents):
1. Analyze Donor Profile: Pull from Data Cloud, run segmentation, and evaluate propensity scoring.
2. Configure Nurture Flow: Select email templates, cadence, and channels for a Segment-Triggered Flow.
3. Compose Email Content: Write personalized email copy, subject lines, and tailored CTAs.
4. Orchestrate Activation: Create/update a Segment-Triggered Flow in Marketing Cloud Next and schedule sends.
Integrations: Connect to Data Cloud segments, Calculated Insights, Marketing Cloud Next Flow Builder, Content, and custom Apex/Flows.
Guardrails: Do NOT auto-activate the Flow. Require explicit human approval before any email send or Flow activation.
Variables to Track: donor_id, segment, lifecycle_stage, persona, top_interests, last_donation_amount, last_donation_date, consent_status, flow_id.
Output Format: Provide a structured JSON plan, draft email content, and a proposed Flow map for review before creating artifacts in Marketing Cloud Next.
I've updated the following sections:
system: Rewrote instructions to define an autonomous nonprofit donor nurture assistant, emphasizing Data Cloud analysis, personalized content, Segment-Triggered Flow proposals, MC Next orchestration, and strict human-approval gates.
config: Set the description to reflect the donor nurture workflow and approval requirement.
variables: Added donor_id, segment, lifecycle_stage, persona, top_interests, last_donation_amount, last_donation_date, consent_status, flow_id, approval_required (default true), and approval_status (default "pending").
agent_router: Added routing transitions to the new subagents: analyze_donor_profile, compose_email_content, configure_nurture_flow, orchestrate_activation.
connections: Added placeholder connections to Data Cloud segments/insights, Marketing Cloud Next Flow Builder/Content, and custom Apex/Flows.
subagent analyze_donor_profile: New subagent with placeholder actions to fetch donor profile and calculated insights, set variables, and emit an analysis JSON artifact.
subagent compose_email_content: New subagent to ensure consent, fetch templates, draft email content/CTAs, and emit a draft_email artifact.
subagent configure_nurture_flow: New subagent to propose a Segment-Triggered Flow map and emit a proposed_flow artifact.
subagent orchestrate_activation: New subagent to create/update a draft Flow, request human approval, and output a final review package; never activates without approval.
Notes:
These new actions reference placeholder targets for external systems (Data Cloud, MC Next, custom Apex/Flows). You’ll need to implement or wire them to real Flows/Apex/Connectors in your org.
For more information on how to set up custom actions, please visit our help page.
For data sources, use Data Cloud DMOs: Individual__dlm, Donor_Giving_History__dll, and Unified_Engagement_Score__c. For downstream actions, have the agent configure a Segment-Triggered Flow to send against a specific donor segment, and create a high-priority Task in Sales Cloud for our major gifts team if a donor's propensity score drops. For compliance, the agent must check the HasOptedOutProcessing flag on Individual__dlm and ground its tone rules using our internal 'Nonprofit_Brand_Voice_Guidelines_2026' Knowledge article. Finally, for output scope, the agent should not autonomously publish; it must save the drafted assets as a 'Campaign_Proposal__c' custom object record for human review and orchestration.
I've updated the following sections:
system: Clarified use of Data Cloud DMOs, added strict HasOptedOutProcessing and consent enforcement, grounded tone via Knowledge article, and emphasized non-activation + Campaign_Proposal__c save and Task escalation rules.
config: Expanded description to reflect Data Cloud DMOs, consent checks, brand guidelines, non-activation, Campaign_Proposal__c save, and Sales Cloud Task creation trigger.
variables: Ensured required fields exist and added has_opted_out_processing (boolean), propensity_score (number), and proposal_id (string).
connection: Replaced invalid connections block, adding references to Data Cloud DMOs (Individual__dlm, Donor_Giving_History__dll, Unified_Engagement_Score__c), Knowledge article Nonprofit_Brand_Voice_Guidelines_2026, Marketing Cloud Next (Flow Builder, Content), and Sales Cloud (Task, Campaign_Proposal__c).
agent_router: Documented routing sequence: analyze_donor_profile → compose_email_content → configure_nurture_flow → orchestrate_activation with approval gates.
analyze_donor_profile: Added DMO queries (placeholders) to fetch Individual__dlm, Donor_Giving_History__dll, and Unified_Engagement_Score__c; set has_opted_out_processing and consent_status; emit analysis JSON with required fields.
compose_email_content: Added consent/opt-out check to skip content if restricted; grounded tone with Knowledge article; pulls Marketing Cloud Content templates; emits draft_email with adherence note.
configure_nurture_flow: Proposes a Segment-Triggered Flow with cadence/channels and entry/exit criteria; emits proposed_flow artifact (not activated).
orchestrate_activation: Creates/updates a draft Segment-Triggered Flow and captures flow_id; creates a high-priority Sales Cloud Task if propensity is low; saves all assets to Campaign_Proposal__c and captures proposal_id; outputs structured plan requiring human approval.
Note: Several integrations are added as placeholder actions that you can wire to your org’s Flows/Apex. For more information on how to set up custom actions, please visit our help page.
Draft a nurture email for a donor who gave $500 last year and hasn't engaged in 6 months.

Figure 2: Full Agent Definition, Explorer tree, Basic Information, full Description, Problems (9) visible. The subagent tree scaffolded as: Agent Router, Escalation, Off Topic, Ambiguous Question as defaults, plus Analyze Donor Profile, Compose Email Content, Configure Nurture Flow, Orchestrate Activation as requested.
Ambiguous prompt instruction
The prompt requested a Task be created "if a donor's propensity score drops." This wording implies a comparison: the donor's current score must be checked against a prior score to detect a decline over time.
The agent did not build this comparison. Instead, it implemented a static threshold check: it evaluates whether the current propensity score is below a fixed number, with no reference to any past value. In the underlying script, this appears as:
if @variables.propensity_score is not None and @variables.propensity_score < 0.3
This means the agent cannot distinguish between two different donors: one whose score has always been low, and one whose score has recently fallen. Both trigger the same Task under this logic.
The agent did not flag this gap or ask which interpretation was intended. It selected the version that required no historical data, and proceeded.
Takeaway: an instruction that is ambiguous will not be flagged for clarification by default. It will be resolved using whichever interpretation is simplest to implement. Confirm this kind of logic by reading the underlying script directly. Do not assume the plain-language description of a subagent's behavior matches what it actually executes.
Checking the Problems panel
I opened the Problems panel to check the agent's real error count. Over the course of this build, the Errors and Warnings counts changed several times, not because new problems were introduced, but because the same underlying issues got reclassified, duplicated, or dropped from view as I saved and reopened the agent.

Figure 3: These were the errors at first: 9 total.
I saved the agent from the main editor. The Problems badge changed from 9 to 43.

Figure 4: Problems 43, Errors tab expanded, sidebar showing 43 Errors, 25 Warnings, and 11 Info together.
Most of the increase from 9 to 43 was the same content that had previously lived under Warnings (the placeholder-target messages, the top_interests type mismatch, and schema warnings about missing complex_data_type_name), now surfacing as Errors instead. Some of it was also duplicated outright: get_individual_dlm appeared twice in a row with identical text, and "Missing value for 'before_reasoning'" appeared twice instead of once.
I then attempted to open Preview. It was blocked, with only two options offered: "Reset Simulator" and "Review Error."

Figure 5: "There was a compilation error. Please review the errors and fix them." Preview blocked, with "Reset Simulator" and "Review Error" as the only two options offered.

Figure 6: Problems 43, Warnings tab, still showing 25 items. This is the same content shown reclassified as Errors in Figure 4, confirming the same issues were being double-counted across categories.
I clicked "Review Error." The badge remained at 43. I then clicked "Reset Simulator," followed by "Review Error" again, and the count changed once more.

Figure 7: Result: 34 Errors, 0 Warnings, 0 Info. The 8 route_type errors were present, followed immediately by the top_interests type mismatch and the get_individual_dlm, get_giving_history_dll, and get_unified_engagement_score placeholder-target messages, all now under red Error icons instead of orange Warning triangles. Since Reset Simulator and Review Error were clicked in sequence rather than in isolation, it isn't possible to say with certainty which one, or the combination of both, produced this result.
Returning to the agent
I reopened the same agent in a later session, having made no edits since the last one. The Problems panel showed 9 Errors, 25 Warnings, 11 Info, the same breakdown as Figure 3, not the 34/0/0 state the prior session had ended on.
Fixing before_reasoning
I opened Orchestrate Activation's underlying script, using Agent Script rather than the visual canvas, because the before_reasoning error was attributed specifically to this subagent, and the canvas view offered no way to inspect or edit that field directly. I switched to the script view and searched for the field by name.

Figure 8: The before_reasoning: block on orchestrate_activation, located via in-editor search. It contained two comment lines, "Only create a draft and request human approval. Never activate or schedule" and "No deterministic steps required before reasoning," and no executable content.
I added two lines to it, because this subagent's stated job is to enforce a human-approval gate, and nothing in its configuration deterministically guaranteed that gate was set before the subagent acted.

Figure 9: The same block, after the edit. The two original comments remain, followed by set @variables.approval_required = True and set @variables.approval_status = "pending".
I ran into errors that could not be resolved
After saving, the before_reasoning error was gone, but the total count went from 9 to 41. Most of the new errors were the same kinds of issues that had been sitting as Warnings before, now showing up as Errors instead.
I used "Save and Refresh" instead of a plain save. That brought the count down to 8. All that remained were the 8 outbound_route_type errors.
Investigating outbound_route_type
I couldn't find this field anywhere in the builder's UI, Connections, Settings, and Behavior were all blank, and clicking the error itself just led to an unrelated panel. I found the field in the agent's underlying script instead: 8 connections, all set to outbound_route_type: "internal".
I tried changing all 8 to "external". Rejected. I tried "flow". Also rejected. I checked Salesforce's documentation for the valid values. It isn't listed anywhere.
Where the build stopped
Agentforce Builder won't run Preview while any Errors remain. These 8 couldn't be resolved, no valid value could be found anywhere, in the UI, the script, or the documentation. So Preview was never reached. A test prompt was sent asking the agent to draft a nurture email for a specific donor, but no response was ever returned, since Preview was required to run it. The Data Graph check and the Flow tie-in also never happened, since both depended on Preview being available.