Files
smoothschedule/smoothschedule/CUSTOM_SCRIPTING_GUIDE.md
poduck 3fef0d5749 feat: Add comprehensive plugin documentation and advanced template system
Added complete plugin documentation with visual mockups and expanded template
variable system with CONTEXT, DATE helpers, and default values.

Backend Changes:
- Extended template_parser.py to support all new template types
- Added PROMPT with default values: {{PROMPT:var|desc|default}}
- Added CONTEXT variables: {{CONTEXT:business_name}}, {{CONTEXT:owner_email}}
- Added DATE helpers: {{DATE:today}}, {{DATE:+7d}}, {{DATE:monday}}
- Implemented date expression evaluation for relative dates
- Updated compile_template to handle all template types
- Added context parameter for business data auto-fill

Frontend Changes:
- Created comprehensive HelpPluginDocs.tsx with Stripe-style API docs
- Added visual mockup of plugin configuration form
- Documented all template types with examples and benefits
- Added Command Reference section with allowed/blocked Python commands
- Documented all HTTP methods (GET, POST, PUT, PATCH, DELETE)
- Added URL whitelisting requirements and approval process
- Created Platform Staff management page with edit modal
- Added can_approve_plugins and can_whitelist_urls permissions

Platform Staff Features:
- List all platform_manager and platform_support users
- Edit user details with role-based permissions
- Superusers can edit anyone
- Platform managers can only edit platform_support users
- Permission cascade: users can only grant permissions they have
- Real-time updates via React Query cache invalidation

Documentation Highlights:
- 4 template types: PROMPT, CONTEXT, DATE, and automatic validation
- Visual form mockup showing exactly what users see
- All allowed control flow (if/elif/else, for, while, try/except, etc.)
- All allowed built-in functions (len, range, min, max, etc.)
- All blocked operations (import, exec, eval, class/function defs)
- Complete HTTP API reference with examples
- URL whitelisting process: contact pluginaccess@smoothschedule.com

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 20:54:07 -05:00

15 KiB

Custom Scripting Guide

Create your own automations with safe, powerful scripting! Write Python-like code with if/else statements, loops, and variables to automate your business processes.

🎯 What You Can Do

  • Access Your Data: Get appointments, customers, and more
  • Write Logic: Use if/else, loops, and variables
  • Send Emails: Contact customers automatically
  • Create Reports: Generate custom analytics
  • Call APIs: Integrate with external services (approved domains)

🔒 Safety Features

We protect your data and our servers with:

  • No Infinite Loops: Automatically stopped after 10,000 iterations
  • Execution Timeout: Scripts limited to 30 seconds
  • API Rate Limits: Maximum 50 API calls per execution
  • Memory Limits: 50MB maximum
  • No File Access: Can't read/write files
  • No Code Injection: Can't use eval, exec, or import
  • Sandboxed: Runs in isolated environment

📚 Available API Methods

api.get_appointments(**filters)

Get appointments for your business.

Parameters:

  • status - Filter by status ('SCHEDULED', 'COMPLETED', 'CANCELED')
  • start_date - Filter by start date ('YYYY-MM-DD')
  • end_date - Filter by end date ('YYYY-MM-DD')
  • limit - Maximum results (default: 100, max: 1000)

Returns: List of appointment dictionaries

Example:

# Get all scheduled appointments
appointments = api.get_appointments(status='SCHEDULED')

# Get appointments from last week
from datetime import datetime, timedelta
start = (datetime.now() - timedelta(days=7)).strftime('%Y-%m-%d')
recent = api.get_appointments(start_date=start, limit=50)

api.get_customers(**filters)

Get customers for your business.

Parameters:

  • limit - Maximum results (default: 100, max: 1000)
  • has_email - Only customers with email addresses (True/False)

Returns: List of customer dictionaries

Example:

# Get all customers with emails
customers = api.get_customers(has_email=True)

# Get first 50 customers
top_customers = api.get_customers(limit=50)

api.send_email(to, subject, body)

Send an email to a customer.

Parameters:

  • to - Email address or customer ID
  • subject - Email subject (max 200 characters)
  • body - Email body (max 10,000 characters)

Returns: True if sent successfully

Example:

# Send to email address
api.send_email(
    to='customer@example.com',
    subject='Special Offer',
    body='Hello! Here is a special offer just for you...'
)

# Send to customer by ID
api.send_email(
    to=123,
    subject='Appointment Reminder',
    body='Your appointment is tomorrow at 2 PM'
)

api.create_appointment(title, start_time, end_time, **kwargs)

Create a new appointment.

Parameters:

  • title - Appointment title
  • start_time - Start datetime (ISO format: '2025-01-15T10:00:00')
  • end_time - End datetime (ISO format: '2025-01-15T11:00:00')
  • notes - Optional notes

Returns: Created appointment dictionary

Example:

apt = api.create_appointment(
    title='Follow-up Consultation',
    start_time='2025-01-20T14:00:00',
    end_time='2025-01-20T15:00:00',
    notes='Automated follow-up'
)

api.log(message)

Log a message for debugging.

Example:

api.log('Script started')
api.log(f'Found {len(appointments)} appointments')

api.http_get(url, headers=None)

Make HTTP GET request to approved domains.

Parameters:

  • url - URL to fetch (must be approved)
  • headers - Optional headers dictionary

Returns: Response text

Example:

# Call Slack webhook
response = api.http_get(
    'https://hooks.slack.com/services/YOUR/WEBHOOK/URL'
)

Helper Methods

# Count items
count = api.count(appointments)

# Sum numbers
total = api.sum([10, 20, 30])

# Filter with condition
active = api.filter(customers, lambda c: c['email'] != '')

📝 Script Examples

Example 1: Simple Appointment Count

# Get all appointments
appointments = api.get_appointments(limit=500)

# Count by status
scheduled = 0
completed = 0

for apt in appointments:
    if apt['status'] == 'SCHEDULED':
        scheduled += 1
    elif apt['status'] == 'COMPLETED':
        completed += 1

# Log results
api.log(f"Scheduled: {scheduled}, Completed: {completed}")

# Return result
result = {
    'scheduled': scheduled,
    'completed': completed,
    'total': len(appointments)
}

Example 2: Conditional Email Campaign

# Get customers
customers = api.get_customers(has_email=True, limit=100)

# Get recent appointments
from datetime import datetime, timedelta
cutoff = (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d')
recent_apts = api.get_appointments(start_date=cutoff)

# Find customers who booked recently
recent_customer_ids = set()
for apt in recent_apts:
    # In real usage, you'd extract customer ID from appointment
    pass

# Send emails to inactive customers
sent = 0
for customer in customers:
    # If customer hasn't booked recently
    if customer['id'] not in recent_customer_ids:
        success = api.send_email(
            to=customer['email'],
            subject='We Miss You!',
            body=f"Hi {customer['name']},\n\nIt's been a while! Come back and get 15% off."
        )
        if success:
            sent += 1

result = {'emails_sent': sent}

Example 3: Weekly Summary Report

# Get appointments from last 7 days
from datetime import datetime, timedelta

end_date = datetime.now().strftime('%Y-%m-%d')
start_date = (datetime.now() - timedelta(days=7)).strftime('%Y-%m-%d')

appointments = api.get_appointments(
    start_date=start_date,
    end_date=end_date,
    limit=500
)

# Group by day
daily_counts = {}
for apt in appointments:
    # Extract date from ISO timestamp
    date = apt['start_time'].split('T')[0]
    daily_counts[date] = daily_counts.get(date, 0) + 1

# Build report
report = "Weekly Appointment Summary\n\n"
for date in sorted(daily_counts.keys()):
    report += f"{date}: {daily_counts[date]} appointments\n"
report += f"\nTotal: {len(appointments)}"

# Send report
api.send_email(
    to='manager@business.com',
    subject='Weekly Summary',
    body=report
)

result = {'total_appointments': len(appointments)}

Example 4: Smart Customer Segmentation

# Get all customers
customers = api.get_customers(has_email=True, limit=500)

# Get all appointments
appointments = api.get_appointments(limit=1000)

# Count visits per customer (simplified)
visit_counts = {}
for apt in appointments:
    # In real usage, extract customer ID
    customer_id = 'placeholder'
    visit_counts[customer_id] = visit_counts.get(customer_id, 0) + 1

# Segment customers
new_customers = []
loyal_customers = []

for customer in customers:
    visits = visit_counts.get(customer['id'], 0)

    if visits == 1:
        new_customers.append(customer)
    elif visits >= 5:
        loyal_customers.append(customer)

# Send different messages to each segment
for customer in new_customers:
    api.send_email(
        to=customer['email'],
        subject='Welcome!',
        body=f"Hi {customer['name']}, thanks for trying us! Here's 20% off your next visit."
    )

for customer in loyal_customers:
    api.send_email(
        to=customer['email'],
        subject='VIP Offer',
        body=f"Hi {customer['name']}, you're a valued customer! Exclusive offer inside..."
    )

result = {
    'new_customers': len(new_customers),
    'loyal_customers': len(loyal_customers)
}

Example 5: Dynamic Pricing Alert

# Get upcoming appointments
from datetime import datetime, timedelta

today = datetime.now().strftime('%Y-%m-%d')
next_week = (datetime.now() + timedelta(days=7)).strftime('%Y-%m-%d')

upcoming = api.get_appointments(
    start_date=today,
    end_date=next_week,
    status='SCHEDULED'
)

# Count by day
daily_bookings = {}
for apt in upcoming:
    date = apt['start_time'].split('T')[0]
    daily_bookings[date] = daily_bookings.get(date, 0) + 1

# Find slow days (less than 3 bookings)
slow_days = []
for date, count in daily_bookings.items():
    if count < 3:
        slow_days.append(date)

# Alert manager
if slow_days:
    message = f"Slow booking days detected:\n\n"
    for date in slow_days:
        count = daily_bookings[date]
        message += f"{date}: only {count} booking(s)\n"
    message += "\nConsider running a promotion!"

    api.send_email(
        to='manager@business.com',
        subject='⚠️ Low Booking Alert',
        body=message
    )

result = {'slow_days': slow_days}

🎨 Using Script Templates

Pre-built templates make it even easier! Just fill in the parameters.

Example: Conditional Email Template

curl -X POST http://lvh.me:8000/api/scheduled-tasks/ \
  -H "Content-Type: application/json" \
  -d '{
    "name": "VIP Customer Campaign",
    "plugin_name": "script_template",
    "plugin_config": {
      "template": "conditional_email",
      "parameters": {
        "condition_field": "visits",
        "condition_value": "5",
        "email_subject": "You are a VIP!",
        "email_body": "Hi {name}, you are one of our best customers!"
      }
    },
    "schedule_type": "CRON",
    "cron_expression": "0 10 * * 1"
  }'

🚀 Creating a Scheduled Script

Via API

curl -X POST http://lvh.me:8000/api/scheduled-tasks/ \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Custom Automation",
    "description": "Weekly customer re-engagement",
    "plugin_name": "custom_script",
    "plugin_config": {
      "script": "# Your Python code here\nappointments = api.get_appointments()\napi.log(f\"Found {len(appointments)} appointments\")\nresult = len(appointments)",
      "description": "Counts appointments and logs the result"
    },
    "schedule_type": "CRON",
    "cron_expression": "0 9 * * 1",
    "status": "ACTIVE"
  }'

Schedule Types

Every Monday at 9 AM:

{
  "schedule_type": "CRON",
  "cron_expression": "0 9 * * 1"
}

Every Hour:

{
  "schedule_type": "INTERVAL",
  "interval_minutes": 60
}

One-Time on Specific Date:

{
  "schedule_type": "ONE_TIME",
  "run_at": "2025-02-01T10:00:00Z"
}

📖 Built-in Functions

You can use these Python built-ins:

# Math
len([1, 2, 3])        # Length
min([1, 2, 3])        # Minimum
max([1, 2, 3])        # Maximum
sum([1, 2, 3])        # Sum
abs(-5)               # Absolute value
round(3.14159, 2)     # Round to 2 decimals

# Type conversion
int('42')             # Convert to integer
float('3.14')         # Convert to float
str(123)              # Convert to string
bool(1)               # Convert to boolean

# Collections
list((1, 2, 3))       # Create list
dict(a=1, b=2)        # Create dictionary
range(10)             # Number sequence
sorted([3, 1, 2])     # Sort list
reversed([1, 2, 3])   # Reverse list

# Iteration
enumerate(['a', 'b']) # Index and value
zip([1, 2], ['a', 'b']) # Combine lists
any([True, False])    # Any true?
all([True, True])     # All true?

What's NOT Allowed

# ❌ Import statements
import os  # Error!

# ❌ Eval/exec
eval('1 + 1')  # Error!

# ❌ Function definitions (for now)
def my_function():  # Error!
    pass

# ❌ Class definitions
class MyClass:  # Error!
    pass

# ❌ File operations
open('file.txt')  # No file access!

# ❌ Network (except approved APIs)
requests.get('http://example.com')  # Error!

🐛 Debugging Tips

Use Logging

api.log('Script started')

customers = api.get_customers()
api.log(f'Found {len(customers)} customers')

for customer in customers:
    api.log(f'Processing {customer["name"]}')
    # ... do something

api.log('Script finished')

Check Execution Logs

# Get logs for task #1
curl http://lvh.me:8000/api/scheduled-tasks/1/logs/

# Filter by status
curl http://lvh.me:8000/api/task-logs/?status=FAILED

Test Scripts Manually

# Execute task immediately (don't wait for schedule)
curl -X POST http://lvh.me:8000/api/scheduled-tasks/1/execute/

Performance Tips

1. Limit Data Fetching

# ❌ Bad: Fetch everything
appointments = api.get_appointments(limit=1000)

# ✅ Good: Fetch only what you need
appointments = api.get_appointments(
    status='SCHEDULED',
    start_date='2025-01-01',
    limit=50
)

2. Use Efficient Loops

# ❌ Bad: Nested loops
for customer in customers:
    for apt in appointments:
        if apt['customer_id'] == customer['id']:
            # ...

# ✅ Good: Build lookup first
customer_apts = {}
for apt in appointments:
    cid = apt['customer_id']
    if cid not in customer_apts:
        customer_apts[cid] = []
    customer_apts[cid].append(apt)

for customer in customers:
    apts = customer_apts.get(customer['id'], [])
    # Much faster!

3. Batch Operations

# ❌ Bad: Individual calls
for customer in customers:
    api.send_email(customer['email'], 'Subject', 'Body')

# ✅ Good: Filter first, then send
active_customers = [c for c in customers if c['status'] == 'active']
for customer in active_customers:
    api.send_email(customer['email'], 'Subject', 'Body')

💡 Common Use Cases

1. Abandoned Cart Recovery

# Find customers with unpaid appointments
unpaid = api.get_appointments(status='SCHEDULED')
# Send reminder emails

2. Birthday Campaigns

# Get customers with birthdays this week
# Send birthday wishes with discount code

3. Review Requests

# Get completed appointments from yesterday
# Send review request emails

4. Capacity Alerts

# Count upcoming appointments
# Alert if fully booked or too empty

5. Customer Lifecycle

# Identify customer segments (new, active, at-risk)
# Send targeted campaigns

🔐 Security Best Practices

  1. Never hardcode secrets in scripts

    • Use environment variables or settings
    • Don't put API keys in code
  2. Validate input data

    email = customer.get('email', '')
    if '@' in email:
        api.send_email(email, 'Subject', 'Body')
    
  3. Limit batch sizes

    # Don't spam - send max 50 emails per run
    customers = api.get_customers(limit=50)
    
  4. Handle errors gracefully

    success = api.send_email(email, subject, body)
    if not success:
        api.log(f'Failed to send to {email}')
    

📊 Script Result Format

Your script should set a result variable with execution results:

# Your script logic here
appointments = api.get_appointments()

# Set result variable
result = {
    'total': len(appointments),
    'scheduled': 10,
    'completed': 25,
    'message': 'Processing completed successfully'
}

This result is stored in the execution log and can be used for tracking and analytics.

🆘 Getting Help

  • Execution Failed? Check the error message in logs
  • Need More API Methods? Contact support to request new features
  • Performance Issues? Review the performance tips above
  • Template Request? Suggest new templates to add

📈 Pricing

Plan Custom Scripts Executions/Month Support
Free 1 active 100 Community
Pro 5 active 1,000 Email
Business Unlimited Unlimited Priority

Start automating your business today! 🚀