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>
662 lines
15 KiB
Markdown
662 lines
15 KiB
Markdown
# 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:**
|
|
```python
|
|
# 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:**
|
|
```python
|
|
# 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:**
|
|
```python
|
|
# 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:**
|
|
```python
|
|
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:**
|
|
```python
|
|
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:**
|
|
```python
|
|
# Call Slack webhook
|
|
response = api.http_get(
|
|
'https://hooks.slack.com/services/YOUR/WEBHOOK/URL'
|
|
)
|
|
```
|
|
|
|
### Helper Methods
|
|
|
|
```python
|
|
# 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
|
|
|
|
```python
|
|
# 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
|
|
|
|
```python
|
|
# 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
|
|
|
|
```python
|
|
# 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
|
|
|
|
```python
|
|
# 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
|
|
|
|
```python
|
|
# 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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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:**
|
|
```json
|
|
{
|
|
"schedule_type": "CRON",
|
|
"cron_expression": "0 9 * * 1"
|
|
}
|
|
```
|
|
|
|
**Every Hour:**
|
|
```json
|
|
{
|
|
"schedule_type": "INTERVAL",
|
|
"interval_minutes": 60
|
|
}
|
|
```
|
|
|
|
**One-Time on Specific Date:**
|
|
```json
|
|
{
|
|
"schedule_type": "ONE_TIME",
|
|
"run_at": "2025-02-01T10:00:00Z"
|
|
}
|
|
```
|
|
|
|
## 📖 Built-in Functions
|
|
|
|
You can use these Python built-ins:
|
|
|
|
```python
|
|
# 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
|
|
|
|
```python
|
|
# ❌ 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
|
|
|
|
```python
|
|
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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```python
|
|
# ❌ 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
|
|
|
|
```python
|
|
# ❌ 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
|
|
|
|
```python
|
|
# ❌ 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
|
|
```python
|
|
# Find customers with unpaid appointments
|
|
unpaid = api.get_appointments(status='SCHEDULED')
|
|
# Send reminder emails
|
|
```
|
|
|
|
### 2. Birthday Campaigns
|
|
```python
|
|
# Get customers with birthdays this week
|
|
# Send birthday wishes with discount code
|
|
```
|
|
|
|
### 3. Review Requests
|
|
```python
|
|
# Get completed appointments from yesterday
|
|
# Send review request emails
|
|
```
|
|
|
|
### 4. Capacity Alerts
|
|
```python
|
|
# Count upcoming appointments
|
|
# Alert if fully booked or too empty
|
|
```
|
|
|
|
### 5. Customer Lifecycle
|
|
```python
|
|
# 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**
|
|
```python
|
|
email = customer.get('email', '')
|
|
if '@' in email:
|
|
api.send_email(email, 'Subject', 'Body')
|
|
```
|
|
|
|
3. **Limit batch sizes**
|
|
```python
|
|
# Don't spam - send max 50 emails per run
|
|
customers = api.get_customers(limit=50)
|
|
```
|
|
|
|
4. **Handle errors gracefully**
|
|
```python
|
|
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:
|
|
|
|
```python
|
|
# 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! 🚀
|