Foxreach
Scanned@concaption
npx machina-cli add skill @concaption/foxreach --openclawFoxReach API Management Skill
You are managing the FoxReach cold email outreach platform through its Python SDK and CLI. This skill covers all API operations for leads, campaigns, sequences, templates, email accounts, inbox, and analytics.
Setup & Authentication
The Python SDK is at integrations/sdk-python/ and the CLI is at integrations/cli/. Both use API key authentication with keys prefixed otr_.
Check if the SDK is available:
python -c "from foxreach import FoxReach; print('SDK ready')"
If not installed, install it:
cd integrations/sdk-python && pip install -e .
Authentication — Always get the API key from the user or environment before making calls. Never hardcode keys. Use environment variable injection:
FOXREACH_API_KEY=otr_... python script.py
Or use the CLI config:
cd integrations/cli && PYTHONPATH=. python -m foxreach_cli.main config set-key --key otr_...
How to Execute Operations
Write inline Python scripts using the SDK. Always follow this pattern:
import json
from foxreach import FoxReach
client = FoxReach(api_key="otr_USER_KEY_HERE")
# ... perform operation ...
client.close()
For quick operations, use one-liners:
python -c "
from foxreach import FoxReach
client = FoxReach(api_key='otr_...')
result = client.leads.list(page_size=10)
for lead in result:
print(f'{lead.id} {lead.email} {lead.status}')
print(f'Total: {result.meta.total}')
client.close()
"
Resource Reference
For complete API details, see api-reference.md. For usage examples of every operation, see examples.md.
Quick Reference — Available Operations
Leads
| Action | Method | Notes |
|---|---|---|
| List | client.leads.list(page=1, page_size=50, search=..., status=..., tags=...) | Paginated, filterable |
| Get | client.leads.get(lead_id) | Returns single Lead |
| Create | client.leads.create(LeadCreate(email=..., first_name=..., ...)) | Deduplicates by email |
| Update | client.leads.update(lead_id, LeadUpdate(company=..., ...)) | Partial update |
| Delete | client.leads.delete(lead_id) | Soft-delete |
Campaigns
| Action | Method | Notes |
|---|---|---|
| List | client.campaigns.list(status=...) | Filter by draft/active/paused/completed |
| Get | client.campaigns.get(campaign_id) | Includes stats |
| Create | client.campaigns.create(CampaignCreate(name=..., ...)) | Creates in draft |
| Update | client.campaigns.update(campaign_id, CampaignUpdate(...)) | Can't edit if active |
| Delete | client.campaigns.delete(campaign_id) | Must be draft |
| Start | client.campaigns.start(campaign_id) | Transitions to active |
| Pause | client.campaigns.pause(campaign_id) | Pauses sending |
| Add Leads | client.campaigns.add_leads(campaign_id, [lead_ids]) | Bulk add |
| Add Accounts | client.campaigns.add_accounts(campaign_id, [account_ids]) | Assign senders |
Sequences (nested under campaigns)
| Action | Method | Notes |
|---|---|---|
| List | client.campaigns.sequences.list(campaign_id) | All steps |
| Create | client.campaigns.sequences.create(campaign_id, SequenceCreate(body=..., ...)) | Add step |
| Update | client.campaigns.sequences.update(campaign_id, seq_id, SequenceUpdate(...)) | Edit step |
| Delete | client.campaigns.sequences.delete(campaign_id, seq_id) | Remove step |
Templates
| Action | Method | Notes |
|---|---|---|
| List | client.templates.list() | Paginated |
| Get | client.templates.get(template_id) | Single template |
| Create | client.templates.create(TemplateCreate(name=..., body=...)) | New template |
| Update | client.templates.update(template_id, TemplateUpdate(...)) | Partial update |
| Delete | client.templates.delete(template_id) | Remove |
Email Accounts
| Action | Method | Notes |
|---|---|---|
| List | client.email_accounts.list() | Paginated |
| Get | client.email_accounts.get(account_id) | With health metrics |
| Delete | client.email_accounts.delete(account_id) | Remove |
Inbox
| Action | Method | Notes |
|---|---|---|
| List Threads | client.inbox.list_threads(category=..., is_read=..., ...) | Filterable |
| Get | client.inbox.get(reply_id) | Full thread |
| Update | client.inbox.update(reply_id, ThreadUpdate(is_read=..., ...)) | Mark read/starred |
Analytics
| Action | Method | Notes |
|---|---|---|
| Overview | client.analytics.overview() | Dashboard KPIs |
| Campaign | client.analytics.campaign(campaign_id) | Metrics + daily stats |
Pagination
List endpoints return PaginatedResponse objects:
result = client.leads.list(page=1, page_size=50, search="acme")
# Access data
for lead in result:
print(lead.email)
# Check pagination info
print(f"Page {result.meta.page}/{result.meta.total_pages}, {result.meta.total} total")
# Get next page
if result.has_next_page():
next_result = result.next_page()
# Auto-paginate through ALL results
for lead in client.leads.list().auto_paging_iter():
print(lead.email)
Error Handling
Always wrap API calls in try/except:
from foxreach import FoxReach, NotFoundError, RateLimitError, AuthenticationError, FoxReachError
try:
lead = client.leads.get("cld_nonexistent")
except NotFoundError:
print("Lead not found")
except AuthenticationError:
print("Invalid API key")
except RateLimitError as e:
print(f"Rate limited. Retry after {e.retry_after}s")
except FoxReachError as e:
print(f"API error: {e}")
Template Variables & Personalization
Email bodies support variable substitution using {{variable}} syntax:
{{firstName}},{{lastName}},{{email}}{{company}},{{title}},{{phone}}{{website}},{{linkedinUrl}}- Custom fields:
{{customFieldName}}
Spintax is also supported: {Hi|Hey|Hello} {{firstName}}
Common Workflows
1. Full Campaign Setup
When the user wants to set up a complete campaign, follow these steps in order:
- Create the campaign with
campaigns.create() - Add sequence steps with
campaigns.sequences.create()for each email in the chain - Add leads with
campaigns.add_leads() - Assign email accounts with
campaigns.add_accounts() - Start the campaign with
campaigns.start()
2. Check Campaign Performance
- Get campaign analytics with
analytics.campaign(id) - Show sent, delivered, bounced, replied, opened stats
- Show reply rate and bounce rate
- If daily_stats are available, summarize trends
3. Manage Inbox
- List unread threads with
inbox.list_threads(is_read=False) - Categorize replies by updating with
inbox.update(id, ThreadUpdate(category="interested")) - Common categories: interested, not_interested, out_of_office, wrong_person, unsubscribe
4. Bulk Lead Import
For adding multiple leads, create them one by one (the API deduplicates by email):
leads_data = [
{"email": "a@example.com", "first_name": "Alice", "company": "Acme"},
{"email": "b@example.com", "first_name": "Bob", "company": "Beta"},
]
created = []
for data in leads_data:
lead = client.leads.create(LeadCreate(**data))
created.append(lead)
print(f"Created: {lead.id} - {lead.email}")
Important Notes
- Base URL:
https://api.foxreach.io/api/v1 - Rate limit: 100 requests per minute. The SDK auto-retries on 429.
- ID prefixes: Leads
cld_, Campaignscmp_, Repliesrpl_, Templatestpl_ - Timezone: All datetimes in UTC ISO 8601 format.
- Sending days: Array of integers, 1=Monday through 7=Sunday.
- Sending hours: 0-23 range, in the campaign's timezone.
- Campaign status flow: draft → active → paused → active → completed
- Soft deletes: Leads are soft-deleted and can reappear on re-import.
- Always confirm with the user before destructive operations (delete, start campaign).
- When listing data, default to showing a formatted summary, not raw JSON.
- When creating resources, confirm the details with the user before executing.
Overview
FoxReach API Manager lets you control leads, campaigns, sequences, templates, email accounts, inboxes and analytics through the Python SDK and CLI. It streamlines creating leads, running campaigns, checking analytics, and managing email sequences—centralizing FoxReach operations in code. Using API keys and SDK/CLI, you can automate end-to-end cold-email workflows.
How This Skill Works
You interact with FoxReach via the Python SDK or the FoxReach CLI. Authenticate with an API key prefixed otr_ injected through the FOXREACH_API_KEY environment variable or via CLI config. Typical usage is writing small Python scripts (or one-liners) that create, update, or fetch leads, campaigns, sequences, templates, and analytics, then closing the client.
When to Use It
- When you need to create or deduplicate leads quickly (by email).
- When you want to create, start, pause, or analyze campaigns.
- When you need to manage sequences, templates, or account senders for outreach.
- When you want to add leads or accounts to campaigns (bulk operations).
- When you want to fetch analytics and metrics for leads or campaigns.
Quick Start
- Step 1: Install or access FoxReach SDK (integrations/sdk-python) or CLI (integrations/cli).
- Step 2: Authenticate with a valid API key (otr_...) via environment variable FOXREACH_API_KEY or CLI config.
- Step 3: Run a quick operation, e.g., list leads or create a lead using the SDK or CLI.
Best Practices
- Authenticate with API keys securely; prefer env var injection over hardcoding.
- Use LeadCreate with email to deduplicate by email.
- Work with draft campaigns before starting; updates are restricted when active.
- Paginate with page and page_size when listing leads or campaigns.
- Always close the client and handle exceptions in scripts.
Example Use Cases
- List the first 10 leads: client.leads.list(page_size=10) and print lead.id, lead.email, lead.status.
- Create a new lead: client.leads.create(LeadCreate(email=EMAIL, first_name=NAME)).
- Create a draft campaign: client.campaigns.create(CampaignCreate(name=NAME)) and add leads.
- Start a campaign: client.campaigns.start(campaign_id) and monitor stats.
- Add leads and accounts to a campaign: client.campaigns.add_leads(campaign_id, [lead_ids]); client.campaigns.add_accounts(campaign_id, [account_ids]).