> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developer.jelliu.co/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developer.jelliu.co/_mcp/server.

# Quickstart

This guide creates an API key, lists the agents in your workspace, and creates an outreach campaign for one of them.

#### Create an API key

In the dashboard, open **Settings → API Keys** (`https://app.jelliu.co/settings?tab=api-keys`) and create a key.

* Only the workspace **owner** can create, list or revoke keys.
* Pick the permission. Creating and activating campaigns are admin operations, so for this guide choose **full**. See [Authentication](/authentication) for what each scope can reach.
* Copy the key when it is shown. It looks like `jl_` followed by 64 hexadecimal characters, and it is displayed **only once**.

Store it in an environment variable:

```bash
export JELLIU_API_KEY="jl_..."
```

#### List your agents

Every campaign runs on an agent. Fetch the agents in your workspace and copy the `id` of the one you want to use.

**`curl`**

```bash title="curl"
curl -sS "https://api.jelliu.co/api/agents?limit=20" \
  -H "Authorization: Bearer $JELLIU_API_KEY"
```

**`Node.js`**

```javascript title="Node.js"
const res = await fetch('https://api.jelliu.co/api/agents?limit=20', {
  headers: { Authorization: `Bearer ${process.env.JELLIU_API_KEY}` },
});
const body = await res.json();
if (!res.ok) throw new Error(`${res.status} ${body.error?.code}`);

for (const agent of body.data) {
  console.log(agent.id, agent.name);
}
```

**`Python`**

```python title="Python"
import os
import requests

res = requests.get(
    "https://api.jelliu.co/api/agents",
    params={"limit": 20},
    headers={"Authorization": f"Bearer {os.environ['JELLIU_API_KEY']}"},
    timeout=30,
)
body = res.json()
if not res.ok:
    raise RuntimeError(f"{res.status_code} {body['error']['code']}")

for agent in body["data"]:
    print(agent["id"], agent["name"])
```

The response is `{ "data": [ ... ] }`, newest agents first. `limit` accepts 1 to 200 (default 50); see [Pagination](/pagination) for paging and the `search` filter.

#### Create a campaign

Create a campaign for that agent. New campaigns start in the `draft` status, so nothing is sent yet.

**`curl`**

```bash title="curl"
curl -sS -X POST "https://api.jelliu.co/api/campaigns" \
  -H "Authorization: Bearer $JELLIU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agentId": "7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4",
    "name": "Webinar follow-up",
    "productContext": "Follow-up calls to people who registered for our webinar but did not attend.",
    "targetAudience": "Operations managers at logistics companies",
    "channel": "voice",
    "category": "sales",
    "schedule": {
      "timezone": "America/Bogota",
      "days": [
        { "day": "monday",    "startHour": 9, "endHour": 18, "enabled": true },
        { "day": "tuesday",   "startHour": 9, "endHour": 18, "enabled": true },
        { "day": "wednesday", "startHour": 9, "endHour": 18, "enabled": true },
        { "day": "thursday",  "startHour": 9, "endHour": 18, "enabled": true },
        { "day": "friday",    "startHour": 9, "endHour": 17, "enabled": true }
      ]
    }
  }'
```

**`Node.js`**

```javascript title="Node.js"
const res = await fetch('https://api.jelliu.co/api/campaigns', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JELLIU_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    agentId: '7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4',
    name: 'Webinar follow-up',
    productContext: 'Follow-up calls to people who registered for our webinar but did not attend.',
    targetAudience: 'Operations managers at logistics companies',
    channel: 'voice',
    category: 'sales',
    schedule: {
      timezone: 'America/Bogota',
      days: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'].map((day) => ({
        day,
        startHour: 9,
        endHour: day === 'friday' ? 17 : 18,
        enabled: true,
      })),
    },
  }),
});
const body = await res.json();
if (!res.ok) throw new Error(`${res.status} ${body.error?.code}`);

console.log(body.data.id, body.data.status); // "draft"
```

**`Python`**

```python title="Python"
import os
import requests

days = ["monday", "tuesday", "wednesday", "thursday", "friday"]
payload = {
    "agentId": "7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4",
    "name": "Webinar follow-up",
    "productContext": "Follow-up calls to people who registered for our webinar but did not attend.",
    "targetAudience": "Operations managers at logistics companies",
    "channel": "voice",
    "category": "sales",
    "schedule": {
        "timezone": "America/Bogota",
        "days": [
            {"day": d, "startHour": 9, "endHour": 17 if d == "friday" else 18, "enabled": True}
            for d in days
        ],
    },
}

res = requests.post(
    "https://api.jelliu.co/api/campaigns",
    json=payload,
    headers={"Authorization": f"Bearer {os.environ['JELLIU_API_KEY']}"},
    timeout=30,
)
body = res.json()
if not res.ok:
    raise RuntimeError(f"{res.status_code} {body['error']['code']}")

print(body["data"]["id"], body["data"]["status"])  # "draft"
```

A successful request returns `201 Created` with the campaign in `data`.

#### Add contacts and activate

Add at least one contact, then activate the campaign.

```bash
# Add a contact (E.164 phone number)
curl -sS -X POST "https://api.jelliu.co/api/campaigns/$CAMPAIGN_ID/contacts" \
  -H "Authorization: Bearer $JELLIU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "phoneNumber": "+573001234567", "name": "Ana Gómez" }'

# Activate
curl -sS -X PATCH "https://api.jelliu.co/api/campaigns/$CAMPAIGN_ID/activate" \
  -H "Authorization: Bearer $JELLIU_API_KEY"
```

Activation starts real outreach to the contacts in the campaign, within the schedule you defined. A campaign with no pending contacts cannot be activated.

## Campaign fields

**`agentId`** `string (uuid)` — required

The agent that runs the campaign. It must belong to your workspace.

---

**`name`** `string` — required

1 to 200 characters.

---

**`productContext`** `string` — required

What the campaign is about. 10 to 5000 characters.

---

**`targetAudience`** `string` — required

Who you are contacting. 1 to 1000 characters.

---

**`schedule`** `object` — required

`timezone` is an IANA time zone (for example `America/Bogota`). `days` is an array of `{ day, startHour, endHour, enabled }`, where `day` is `monday` through `sunday`, `startHour` is 0 to 23, `endHour` is 1 to 24, and `startHour` must be lower than `endHour` on enabled days. At least one day must be enabled.

---

**`channel`** `string` — default: voice

One of `voice`, `whatsapp`, `webchat`, `email`.

---

**`category`** `string` — default: sales

One of `sales`, `support`, `scheduling`, `surveys`, `collections`, `retention`, `notifications`, `interview`, `language_assessment`, `general`.

---

**`maxConcurrentCalls`** `number` — default: 10

1 to 500.

---

**`maxRetryAttempts`** `number` — default: 3

0 to 10.

---

**`retryIntervalMinutes`** `number` — default: 60

5 to 1440.

---

**`whatsappTemplateId`** `string (uuid)`

WhatsApp campaigns: the approved template used to reach out. Without it, a WhatsApp campaign only answers people who write in.

---

**`emailSubject`** `string`

Email campaigns: 1 to 300 characters.

---

**`emailBody`** `string`

Email campaigns: 1 to 20000 characters.

---

## Next steps

* Learn how scopes work in [Authentication](/authentication).
* Handle failures by `code` with [Errors](/errors).
* Get notified when calls finish with [Webhooks](/webhooks).