Complete provisioning workflow for Einstein Agent Users and permission sets. Validated against ORM1, ORM2, AutomotiveSupport, and SalesforceProductAssistant agents.
---
## License Requirement
PID_DigitalAgent (typically included with Agentforce licenses)
**How to check agent type**: Look at the `agent_type` field in the `config:` block of your `.agent` file, or query: `sf data query --json --query "SELECT DeveloperName, Type FROM BotDefinition WHERE DeveloperName = 'AgentName'" -o TARGET_ORG`
--query "SELECT PermissionSet.Name, PermissionSet.Label FROM PermissionSetAssignment WHERE Assignee.Username = '<agent_name>_user@<orgId>.ext' ORDER BY PermissionSet.Name" \
**Note**: `sf org create user` only works in scratch orgs. For production/sandbox, use `sf data create record`. Attempting `sf org create user` in a non-scratch org fails with an authorization error.
**Username format**: `{agent_name}_agent@{orgId}.ext` (production) or `{agent_name}.{suffix}@{orgfarm}.salesforce.com` (dev/scratch). Always query the target org to confirm the exact format.
---
### Step 2: Assign System Permission Set (`AgentforceServiceAgentUser`)
Critical: Must be assigned BEFORE publishing the agent. Without it, publish fails with "Internal Error".
sf data query --json --query "SELECT Id, PermissionSet.Name FROM PermissionSetAssignment WHERE Assignee.Username = '{agent_name}_agent@{orgId}.ext' AND PermissionSet.Name = 'AgentforceServiceAgentUser'" -o TARGET_ORG
<!-- Add one entry per Apex class the agent calls -->
<classAccesses>
<apexClass>YourApexClassName</apexClass>
<enabled>true</enabled>
</classAccesses>
<!-- Repeat for ALL Apex classes referenced via apex:// in agent script -->
</PermissionSet>
```
Key rule: Include EVERY Apex class referenced via `apex://` in your agent script. Missing even one causes "invocable action does not exist" at runtime.
sf data query --json --query "SELECT PermissionSet.Name FROM PermissionSetAssignment WHERE Assignee.Username = '{agent_name}_agent@{orgId}.ext'" -o TARGET_ORG
### Step 5: Set `default_agent_user` in Agent Config
In your `.agent` file:
```yaml
config:
developer_name: "AgentName"
agent_description: "Your agent description"
agent_type: "AgentforceServiceAgent"
default_agent_user: "{agent_name}_agent@{orgId}.ext" # Service agents ONLY
```
---
### Step 6: Deploy, Test, Publish & Activate
**Validated workflow pattern**: Deploy as unpublished metadata, test with preview, then publish only when tests pass. This avoids version management overhead during iteration.
2. All Apex actions execute without "Insufficient Privileges" errors
3. Agent responds with expected data
4. No compilation errors
If testing reveals problems, edit your agent script or Apex classes, redeploy, and test again — no publish required.
**⚠️ `WITH USER_MODE` Object Permissions:** Apex using `WITH USER_MODE` requires the Einstein Agent User to have read access on queried objects. Class-level access alone is not enough. Missing object permissions fail silently — 0 rows, no error. If live preview returns empty but simulated works, check Setup > Profiles > Einstein Agent User > Object Permissions. Fix by adding `<objectPermissions>` to your custom PS:
```xml
<objectPermissions>
<allowRead>true</allowRead>
<object>Vehicle__c</object>
</objectPermissions>
```
See [preview-test-loop.md](preview-test-loop.md) for the complete smoke test workflow.
- Auto-generated `NextGen_ORM1_Permissions` only included 3 classes (missing `ShipmentTracker`)
- Runtime error: "invocable action track_delivery does not exist"
- Fix: Created custom `ORM1_Access` with all 4 classes — no errors
Best practice: Always create your own custom `{AgentName}_Access` PS with explicit `<classAccesses>` for every Apex class. Ignore the auto-generated PS.
---
## End-to-End Verification Checklist
Run this combined query to verify all setup steps for a Service Agent:
sf data query --json --query "SELECT PermissionSet.Name FROM PermissionSetAssignment WHERE Assignee.Username = '{agent_name}_agent@{orgId}.ext' AND PermissionSet.Name = 'AgentforceServiceAgentUser'" -o TARGET_ORG
sf data query --json --query "SELECT PermissionSet.Name FROM PermissionSetAssignment WHERE Assignee.Username = '{agent_name}_agent@{orgId}.ext' AND PermissionSet.Name = '{AgentName}_Access'" -o TARGET_ORG
sf data query --json --query "SELECT PermissionSet.Name, PermissionSet.Label FROM PermissionSetAssignment WHERE Assignee.Username = '{agent_name}_agent@{orgId}.ext'" -o TARGET_ORG
- **Prevention:** Step 6 splits publish and activate into separate steps with verification
- **Result:** Agent is both published AND activated
---
## Troubleshooting
| Error | Cause | Fix |
|-------|-------|-----|
| "Internal Error" on publish | `AgentforceServiceAgentUser` PS not assigned to Einstein Agent User | Assign system PS (Step 2), wait 2-3 min, retry publish |
| "Insufficient Privileges" at runtime | Custom PS missing or incomplete `<classAccesses>` | Verify custom PS includes ALL Apex classes, redeploy + reassign |
| "invocable action does not exist" | Apex class not in custom PS (auto-generated PS incomplete) | Create custom `{AgentName}_Access` with all `<classAccesses>` (Step 3) |
| "Invalid default_agent_user" | Username typo or user not active | Query Einstein Agent Users, verify exact username + `IsActive = true` |
| Agent runs but returns wrong data | Employee agent using wrong user context | Verify `agent_type` — Service agents use dedicated user, Employee agents use logged-in user |
| `sf org create user` fails | Used in production/sandbox org | Use `sf data create record` instead (Step 1, Option B) |