Saleshandy API: Create Sequences & Import Prospects Programmatically
Saleshandy's API documentation is scattered across multiple pages with inconsistent examples. I spent hours figuring out what works. Here's everything in one place — authentication, prospect import, email accounts, and the specific gotchas that trip up every developer.
Authentication
- The header is
x-api-key(lowercase) — notapi-key, notAuthorization: Bearer - The base URL is
https://open-api.saleshandy.com— NOTapi.saleshandy.com - GET requests that include
Content-Typereturn 406. Don't set it for GETs.
# Correct authentication
curl "https://open-api.saleshandy.com/v1/sequences" \
-H "x-api-key: YOUR_API_KEY" \
-H "Accept: application/json"
Getting Your API Key
Go to my.saleshandy.com → Settings → API Key → Create API Key. Label it anything. Copy the key immediately — you can't view it again.
my.saleshandy.com, not app.saleshandy.com. The latter is a different/legacy URL that shows an empty state. I wasted 2 hours on this.
Finding the OpenAPI Spec
The Swagger UI at open-api.saleshandy.com/api-doc/ is JavaScript-rendered and hard to scrape. The raw JSON spec is at:
https://open-api.saleshandy.com/api-doc-json
This gives you every endpoint, schema, and parameter in one JSON file.
Core Endpoints
List Sequences
GET /v1/sequences
# Response includes sequence IDs, titles, and step IDs
{
"payload": [
{
"id": "LGz3m8KNPr",
"title": "My Sequence",
"steps": [
{"id": "gOwE4X1Mzb", "name": "Step 1"}
]
}
]
}
Import Prospects
The most important endpoint. Use the field-name version — it's simpler:
POST /v1/sequences/prospects/import-with-field-name
{
"prospectList": [
{
"First Name": "John",
"Last Name": "Smith",
"Email": "john@example.com",
"Company": "ACME Inc",
"Job Title": "CEO"
}
],
"stepId": "YOUR_STEP_ID",
"verifyProspects": true,
"conflictAction": "noUpdate"
}
overwrite, noUpdate, or addMissingFields. Anything else (like "skip") returns a 400 error with an unhelpful message.
Import is async. You get a requestId back and can check status:
GET /v1/prospects/import-status/{requestId}
Email Accounts
# List all email accounts
POST /v1/email-accounts
Body: {"page": 1, "pageSize": 20}
# Add accounts to a sequence
POST /v1/sequences/{sequenceId}/email-accounts/add
Body: {"emailAccountIds": ["id1", "id2"]}
Get Step Content
GET /v1/sequences/{sequenceId}/steps/{stepId}
# Returns step variants with subject and HTML content
Activate/Pause Sequences
POST /v1/sequences/status
Body: {"sequenceIds": ["id"], "status": "active"}
What the API Can't Do
As of April 2026, these operations require the web UI:
- Create sequences — API can list and manage, but not create
- Create/edit steps — no PUT/PATCH endpoint for step content
- Create email templates — UI only
This means you need to create your sequence structure in the UI first, then use the API for everything else (importing prospects, managing accounts, activating/pausing).
Complete Import Script
import json, subprocess, csv, time
API_KEY = "YOUR_KEY"
BASE = "https://open-api.saleshandy.com"
def api(method, endpoint, data=None):
cmd = ['curl', '-s', '-X', method, f'{BASE}{endpoint}',
'-H', f'x-api-key: {API_KEY}',
'-H', 'Accept: application/json']
if data:
cmd += ['-H', 'Content-Type: application/json',
'-d', json.dumps(data)]
r = subprocess.run(cmd, capture_output=True, text=True)
return json.loads(r.stdout)
# List sequences to get step IDs
seqs = api('GET', '/v1/sequences')
for s in seqs['payload']:
print(f"{s['title']}: {s['id']}")
for step in s['steps']:
print(f" {step['name']}: {step['id']}")
# Import from CSV
with open('leads.csv') as f:
prospects = []
for row in csv.DictReader(f):
name = row['name'].split(' ', 1)
prospects.append({
"First Name": name[0],
"Last Name": name[1] if len(name) > 1 else "",
"Email": row['email'],
"Company": row.get('organization', ''),
"Job Title": row.get('title', '')
})
# Import in batches of 50
STEP_ID = "YOUR_STEP_ID"
for i in range(0, len(prospects), 50):
batch = prospects[i:i+50]
result = api('POST',
'/v1/sequences/prospects/import-with-field-name',
{"prospectList": batch, "stepId": STEP_ID,
"verifyProspects": True, "conflictAction": "noUpdate"})
print(f"Batch {i}: {result['payload']['requestId']}")
time.sleep(1)
Skip the setup pain
Get production-ready scripts for Saleshandy + Apollo integration.
Get the Skill Pack — $9Written by Joey T. Follow @JoeyTbuilds for more.