A reliable, internal tool to generate demo-ready sales data for your People.ai product demo environment. Create realistic opportunities with populated activities and scorecards on demand.
This tool helps you:
- Generate predictable demo datasets on demand - Press one button and get a known-good demo org
- Control which opportunities get coverage - Target specific new business opportunities in chosen date ranges
- Create coherent activity history - Past/future meetings and emails that People.ai can ingest
- Pre-populate scorecards - MEDDICC or other frameworks with realistic, consistent answers
- Make it repeatable - Re-run whenever a sandbox gets stale, broken, or reset
- Deterministic generation - Same seed produces same results
- Idempotent reruns - Safe to run multiple times without duplicates
- Dry-run mode - Preview what would be created without making changes
- Structured logging - JSONL event logs + summary JSON for auditability
- LLM-powered content - Optional realistic meeting notes and email bodies
- Smoke testing - Quick validation with a single opportunity
- Python 3.9 or higher
- Salesforce credentials with API access
- OpenAI API key (optional, for LLM-generated content)
For the fastest setup, use the automated script:
./setup_dev.shThis will:
- Create virtual environment
- Install all dependencies
- Copy example configuration files
- Verify installation
- Clone the repository:
git clone <repository-url>
cd People.AI-Data-Generator- Create and activate a virtual environment:
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate- Install the package in development mode:
pip install -e .- Create your configuration:
cp demo.example.yaml demo.yaml
cp .env.example .env- Edit
.envwith your credentials:
SF_USERNAME=your.email@company.com
SF_PASSWORD=your_password
SF_SECURITY_TOKEN=your_security_token
OPENAI_API_KEY=sk-... # Optional- Edit
demo.yamlwith your Salesforce instance and preferences.
The configuration file (demo.yaml) controls all aspects of data generation:
Run Settings
name: Identifier for this demo packseed: Random seed for deterministic generationidempotency_mode: Useexternal_state(SQLite) ortag(custom field)
Salesforce Query
instance_url: Your Salesforce instanceopportunity_type: Filter by type (e.g., "New Business")stages_allowed: List of stages to includeclose_date_range: Date range for opportunities
Activity Generation
past_days/future_days: Time windows for activity generationmeetings.past_min/past_max: Number of past meetings per oppemails.min/max: Number of emails per opprealism_level:none,light, orheavy(controls LLM usage)
Scorecards
templates: List of templates (currently supports "MEDDICC")coverage_target: Percentage of questions to answer (0.0-1.0)confidence_floor: Minimum confidence score for answersmode:heuristic,llm, orhybrid
See demo.example.yaml for full configuration options.
Run full generation:
demo-gen run -c demo.yamlPreview without changes (dry-run):
demo-gen dry-run -c demo.yamlCheck status of a previous run:
demo-gen status --run-id run-8f3aSmoke test (single opportunity):
demo-gen smoke -c demo.yaml --opp-id 006...Reset/cleanup a run (tag mode or external_state with state.sqlite):
demo-gen reset --run-id run-8f3aEnvironment selection:
demo-gen run -c demo.yaml --env sandbox # or staging, prod-demoCustom log directory:
demo-gen run -c demo.yaml --log-dir ./my-runsConcurrency and safety limits:
demo-gen run -c demo.yaml --concurrency 10 --max-opps 150Each run creates a directory under runs/ with the following structure:
runs/
2025-12-18T14-32-10Z_se-demo-pack_run-8f3a/
config.resolved.yaml # Full resolved configuration
run.json # Run metadata and status
events.jsonl # Detailed event log (one JSON per line)
errors.jsonl # Error log
summary.json # Final statistics
state.sqlite # Idempotency state (if using external_state mode)
events.jsonl:
{"ts":"2025-12-18T14:33:01Z","run_id":"run-8f3a","opportunity_id":"006...","action":"opportunity_selected"}
{"ts":"2025-12-18T14:33:10Z","run_id":"run-8f3a","opportunity_id":"006...","action":"meeting_created","activity_id":"00U...","when":"past"}
{"ts":"2025-12-18T14:34:10Z","run_id":"run-8f3a","opportunity_id":"006...","action":"scorecard_upserted","scorecard_id":"sc_..."}summary.json:
{
"run_id": "run-8f3a",
"started_at": "2025-12-18T14:32:10Z",
"finished_at": "2025-12-18T14:41:55Z",
"opps_selected": 50,
"meetings_created": 280,
"emails_created": 640,
"scorecards_created": 50,
"scorecard_answers_written": 950,
"failures": 3,
"coverage": 0.86
}- Configure your target opportunities in
demo.yaml - Run dry-run to preview:
demo-gen dry-run -c demo.yaml
- Execute the generation:
demo-gen run -c demo.yaml
- Wait for People.ai ingestion (check
peopleai.expected_latency_minutes) - Verify in People.ai that activities appear on opportunities
Test ingestion pipeline with a single opportunity first:
demo-gen smoke -c demo.yaml --opp-id 006ABC123000001Check the output for created record IDs, then verify they appear in People.ai.
If your demo environment has been reset or gone stale:
- Update
close_date_rangeindemo.yamlif needed - Run with same seed for consistency:
demo-gen run -c demo.yaml
- Idempotency ensures only new/missing data is created
Uses SQLite database to track created records:
run:
idempotency_mode: "external_state"- Pros: Works with any Salesforce org, no custom fields needed
- Cons: Cleanup/reset only deletes Events/Tasks tracked in state.sqlite
Tags records with run ID using a custom field:
run:
idempotency_mode: "tag"
run_tag_field: "Demo_Run_Id__c"- Pros: Enables cleanup of tagged Events/Tasks via
resetcommand - Cons: Requires custom field on Activity objects
- Verify activities were created in Salesforce (check event logs)
- Confirm activities have correct
WhatId(opportunity) andOwnerId - Check People.ai ingestion settings - ensure CRM activities are enabled
- Wait for
expected_latency_minutesbefore checking
- Verify
OPENAI_API_KEYis set correctly - Check API quota/rate limits
- Set
llm.enabled: falseto use heuristic content only
- Verify credentials in
.envare correct - Check security token is current (reset if IP changed)
- Ensure API access is enabled for your user
- Verify instance URL format:
https://your-instance.my.salesforce.com
Ensure your Salesforce user has:
- Read access to Opportunities, Accounts, Contacts
- Create access to Events and Tasks
- API Enabled permission
- config.py - Configuration schema and validation
- logger.py - JSONL structured logging
- state_store.py - SQLite-based idempotency
- sf_client.py - Salesforce API wrapper
- activity_planner.py - Deterministic activity planning
- content_gen.py - LLM content generation
- scorecard_client.py - Scorecard creation and population
- runner.py - Main orchestration logic
- cli.py - Command-line interface
Load Config → Query Opportunities → For each Opportunity:
├─ Plan Activities (deterministic)
├─ Generate Content (LLM optional)
├─ Create Meetings & Emails in Salesforce
├─ Create Scorecards
└─ Log Events & Update State
pip install -e ".[dev]"
pytestblack src/
ruff check src/This is an internal tool. Contact the Sales Engineering team for questions or enhancement requests.
Internal use only.