Developer Docs
ZenMode REST API
Push LinkedIn automation tasks programmatically. Available on all plans.
Authentication
All API requests require a Bearer token. Generate an API key from your dashboard.
curl -H "Authorization: Bearer zm_your_api_key_here" \
https://www.zen-mode.io/api/v1/tasksKeep your API key secret. It is shown only once when created. If compromised, revoke it from the dashboard and generate a new one.
Rate Limits
API Requests
100 per minute
Task Creation
500 per day per LinkedIn account
Exceeding limits returns 429 Too Many Requests.
Endpoints
/api/v1/tasks— Create a taskQueue a LinkedIn action. The desktop app picks it up automatically.
Request Body
{
"action_type": "connect",
"linkedin_account_id": "acc_abc123",
"payload": {
"profile_url": "https://linkedin.com/in/jane-doe",
"message": "Hi Jane, I'd love to connect!"
},
"priority": 0,
"max_retries": 3
}Action Types
connect — Send a connection request (with optional message)
message — Send a direct message to an existing connection
follow — Follow a profile
view_profile — View a profile (warm-up action)
withdraw_connection — Withdraw a pending connection request
Response (201)
{
"task": {
"id": "task_m1abc_x7k2p9qr",
"action_type": "connect",
"status": "pending",
"linkedin_account_id": "acc_abc123",
"payload": { ... },
"priority": 0,
"created_at": "2026-04-02T10:30:00.000Z"
}
}/api/v1/tasks— List tasksRetrieve your tasks with optional filters.
Query Parameters
status — Filter by status: pending, processing, completed, failed, cancelled
linkedin_account_id — Filter by LinkedIn account
limit — Results per page (default 50, max 100)
offset — Pagination offset
curl -H "Authorization: Bearer zm_..." \
"https://www.zen-mode.io/api/v1/tasks?status=completed&limit=10"/api/v1/tasks/:id— Get task statuscurl -H "Authorization: Bearer zm_..." \
https://www.zen-mode.io/api/v1/tasks/task_m1abc_x7k2p9qr/api/v1/tasks/:id— Cancel a pending taskOnly tasks in pending or queued status can be cancelled.
curl -X DELETE -H "Authorization: Bearer zm_..." \
https://www.zen-mode.io/api/v1/tasks/task_m1abc_x7k2p9qrWebhooks
Register a webhook URL to receive POST callbacks when tasks complete or fail.
/api/v1/webhooks— Register a webhook{
"url": "https://your-server.com/zenmode-webhook",
"events": ["task.completed", "task.failed"]
}URL must use HTTPS. Valid events: task.completed, task.failed.
/api/v1/webhooks— List webhooks/api/v1/webhooks?id=123— Delete a webhookWebhook Payload
When a task completes or fails, ZenMode sends a signed POST to your webhook URL.
Headers
X-ZenMode-Signature — HMAC-SHA256 hex digest of the request body, signed with your webhook secret
X-ZenMode-Event — Event type (e.g. task.completed)
Body
{
"event": "task.completed",
"data": {
"task_id": "task_m1abc_x7k2p9qr",
"action_type": "connect",
"status": "completed",
"result": { "success": true },
"linkedin_account_id": "acc_abc123"
},
"timestamp": "2026-04-02T10:35:00.000Z"
}Verifying Signatures
const crypto = require('crypto');
function verifyWebhook(body, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}Code Examples
cURL
# Create a connection request task
curl -X POST https://www.zen-mode.io/api/v1/tasks \
-H "Authorization: Bearer zm_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"action_type": "connect",
"linkedin_account_id": "acc_abc123",
"payload": {
"profile_url": "https://linkedin.com/in/jane-doe",
"message": "Hi Jane, would love to connect!"
}
}'
# Check task status
curl https://www.zen-mode.io/api/v1/tasks/task_m1abc_x7k2p9qr \
-H "Authorization: Bearer zm_your_api_key"
# List completed tasks
curl "https://www.zen-mode.io/api/v1/tasks?status=completed&limit=20" \
-H "Authorization: Bearer zm_your_api_key"Python
import requests
API_KEY = "zm_your_api_key"
BASE_URL = "https://www.zen-mode.io/api/v1"
headers = {"Authorization": f"Bearer {API_KEY}"}
# Create a task
task = requests.post(f"{BASE_URL}/tasks", headers=headers, json={
"action_type": "connect",
"linkedin_account_id": "acc_abc123",
"payload": {
"profile_url": "https://linkedin.com/in/jane-doe",
"message": "Hi Jane, would love to connect!"
}
}).json()
print(f"Task created: {task['task']['id']}")
# Poll for completion
import time
while True:
status = requests.get(
f"{BASE_URL}/tasks/{task['task']['id']}", headers=headers
).json()
if status["task"]["status"] in ("completed", "failed"):
print(f"Task {status['task']['status']}: {status['task'].get('result')}")
break
time.sleep(10)JavaScript / Node.js
const API_KEY = "zm_your_api_key";
const BASE_URL = "https://www.zen-mode.io/api/v1";
async function createTask(actionType, profileUrl, message) {
const res = await fetch(`${BASE_URL}/tasks`, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
action_type: actionType,
payload: { profile_url: profileUrl, message },
}),
});
return res.json();
}
// Send a connection request
const { task } = await createTask(
"connect",
"https://linkedin.com/in/jane-doe",
"Hi Jane, would love to connect!"
);
console.log("Task ID:", task.id);
// Register a webhook to get notified
await fetch(`${BASE_URL}/webhooks`, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
url: "https://your-server.com/webhook",
events: ["task.completed", "task.failed"],
}),
});Task Lifecycle
pending — Task queued, waiting for desktop app to pick up
processing — Desktop app is executing the task
completed — Action performed successfully on LinkedIn
failed — Action failed (check result for details)
cancelled — Task was cancelled before processing
Error Codes
| Status | Meaning |
|---|---|
| 400 | Bad request — missing or invalid fields |
| 401 | Unauthorized — missing or invalid API key |
| 403 | Forbidden — no active subscription |
| 404 | Not found — task or webhook doesn't exist |
| 429 | Rate limit exceeded — wait 60 seconds |
| 500 | Server error |
Need help?
Generate your API key and start sending tasks in minutes.