# Getting Started with the AcelleMail REST API

> AcelleMail ships a REST API that exposes nearly every operation the admin UI offers — create lists, add subscribers, send campaigns, fire automations, read reports. This guide walks the 4 things you need to make your first successful API call: where to find your token, how to send it, what to call first, and how to debug when it doesn't work.

- Canonical URL: https://acellesend.com/kb/articles/getting-started-with-the-acellemail-rest-api
- Author: Tom Becker, Developer Editor
- Category: Developer Guide — REST API, webhooks, customization, extending source code, plugins
- Content type: tutorial · Difficulty: intermediate
- Tags: Acellemail
- Published: 2026-02-26 · Last updated: 2026-08-15
- Reading time: 8 min
- Publisher: AcelleMail Knowledge Base, https://acellesend.com/kb

---

## What this is for

AcelleMail ships a **REST API** that exposes nearly every operation the admin UI offers — create lists, add subscribers, send campaigns, trigger automations, read reports. If you've ever wished you could "do that thing in the admin, but from my own code", the API is the answer.

This guide is the **5-minute intro**: where to find your API token, two ways to send it, the first call to make to confirm everything works, and the common failure modes. Once this works, jump to the [REST API Authentication and Endpoints reference](https://acellesend.com/kb/articles/rest-api-authentication-and-endpoints) for the full surface area.

## Step 1 — Get your API token

Every AcelleMail account (admin OR customer) has an `api_token` stored in the database. Two ways to retrieve it:

### Method A — From the UI (easiest)

1. Log in to AcelleMail
2. Click your name (top-right) → **Account / Profile**
3. Look for the **API Token** field (or **Developer / API** section depending on your version)
4. Click "Generate token" if no token exists, or "Show" / "Copy" if one does

The token is a long random string like `Y2x4cTBxbGZ6MDAwMDAxazB0cWxqYmJ4dQ`. Treat it like a password — anyone with this token can act as you.

### Method B — Via the login endpoint (for headless / scripted setups)

```bash
curl -X POST https://mail.example.com/api/v1/user/login \
  -H "Accept: application/json" \
  -d 'email=you@example.com' \
  -d 'password=YourPassword'

# Response:
# {"api_token": "Y2x4cTBxbGZ6MDAwMDAxazB0cWxqYmJ4dQ"}
```

Store the returned `api_token` in your code's secrets manager / `.env` file. **Never commit it to version control.**

## Step 2 — Two ways to send the token

Both are equivalent; pick whichever fits your HTTP client better.

### Method A — `Authorization: Bearer` header (preferred)

```bash
curl -H "Authorization: Bearer Y2x4cTBxbGZ6MDAwMDAxazB0cWxqYmJ4dQ" \
     -H "Accept: application/json" \
     https://mail.example.com/api/v1/me
```

Standard OAuth-style header. Works in every HTTP client. Doesn't show up in server access logs (where the URL does).

### Method B — `?api_token=XXX` query parameter

```bash
curl "https://mail.example.com/api/v1/me?api_token=Y2x4cTBxbGZ6MDAwMDAxazB0cWxqYmJ4dQ" \
     -H "Accept: application/json"
```

Easier for quick CLI testing. **Avoid in production** — query strings get logged in access logs, browser history, referrers. Use the header.

## Step 3 — Your first call

The `/api/v1/me` endpoint returns the authenticated user's profile. It's the standard "is my auth working?" check:

```bash
curl -H "Authorization: Bearer YOUR_TOKEN" \
     -H "Accept: application/json" \
     https://mail.example.com/api/v1/me
```

Expected response (HTTP 200):

```json
{
  "id": 1,
  "uid": "Y2x4cTBxbGZ6MDAwMDAxazB0cWxqYmJ4dQ",
  "email": "you@example.com",
  "first_name": "Your",
  "last_name": "Name",
  "created_at": "2024-01-15T10:32:11.000000Z",
  "customer": {
    "id": 5,
    "uid": "...",
    "company": "Acme Corp"
  }
}
```

If you see your own data, **the API is working and your auth is correct.** Move on to the [endpoint reference](https://acellesend.com/kb/articles/rest-api-authentication-and-endpoints).

## Step 4 — Client snippets

Once auth works in `curl`, the same pattern translates to any language. Here are tested snippets:

### PHP (Guzzle)

```php
$client = new \GuzzleHttp\Client([
    'base_uri' => 'https://mail.example.com/api/v1/',
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('ACELLEMAIL_TOKEN'),
        'Accept' => 'application/json',
    ],
]);

$response = $client->get('me');
$me = json_decode($response->getBody(), true);
echo $me['email'];
```

### Python (requests)

```python
import os
import requests

BASE = 'https://mail.example.com/api/v1'
HEADERS = {
    'Authorization': f"Bearer {os.environ['ACELLEMAIL_TOKEN']}",
    'Accept': 'application/json',
}

r = requests.get(f'{BASE}/me', headers=HEADERS)
r.raise_for_status()
print(r.json()['email'])
```

### Node.js (fetch)

```javascript
const BASE = 'https://mail.example.com/api/v1';
const token = process.env.ACELLEMAIL_TOKEN;

const res = await fetch(`${BASE}/me`, {
  headers: {
    'Authorization': `Bearer ${token}`,
    'Accept': 'application/json',
  },
});
const me = await res.json();
console.log(me.email);
```

### Go (net/http)

```go
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "os"
)

func main() {
    req, _ := http.NewRequest("GET", "https://mail.example.com/api/v1/me", nil)
    req.Header.Set("Authorization", "Bearer "+os.Getenv("ACELLEMAIL_TOKEN"))
    req.Header.Set("Accept", "application/json")

    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()

    var me struct{ Email string }
    json.NewDecoder(resp.Body).Decode(&me)
    fmt.Println(me.Email)
}
```

## Step 5 — Try something useful

Now that auth works, fetch your subscriber lists:

```bash
curl -H "Authorization: Bearer YOUR_TOKEN" \
     -H "Accept: application/json" \
     https://mail.example.com/api/v1/lists
```

Response shape:

```json
{
  "data": [
    { "uid": "abc123", "name": "Newsletter", "subscribers_count": 5230, ... },
    { "uid": "def456", "name": "Customers", "subscribers_count": 1842, ... }
  ],
  "current_page": 1,
  "per_page": 20,
  "total": 2
}
```

The `uid` field is what you'll use for subsequent calls (e.g. `GET /api/v1/lists/abc123/subscribers`). See the [endpoint reference](https://acellesend.com/kb/articles/rest-api-authentication-and-endpoints) for what each resource looks like.

## Common issues

| What you see | Likely cause | Fix |
|---|---|---|
| `401 Unauthenticated` | Wrong / expired / missing token | Re-fetch from UI; verify you're hitting the right base URL (`/api/v1/` not `/api/`) |
| `404 Not Found` on a known endpoint | Wrong base URL — used `/api/me` instead of `/api/v1/me` | All endpoints are versioned under `/v1/` |
| `415 Unsupported Media Type` | Missing `Accept: application/json` header | Always include this header on every request |
| `422 Unprocessable Entity` | Validation error on request body | Response body lists exactly which fields failed; fix and retry |
| `429 Too Many Requests` | Rate-limit hit (if your install has rate limiting) | Back off; check `Retry-After` header for wait time |
| `500 Internal Server Error` | Server-side bug | Check `/var/www/acellemail/storage/logs/laravel.log` for the stack trace |
| Empty `data: []` on `/lists` | Token belongs to a different customer with no lists | Verify which customer the token is for via `/me` → `customer.id`; lists are scoped per customer |
| Different shape than docs say | API response shape varies slightly across major Acelle versions | Print the full JSON in development; rely on field presence checks |
| `cURL: (60) SSL certificate problem` | Self-signed cert on staging install | `curl -k ...` for testing; fix the cert before production |
| Login endpoint returns "Invalid credentials" | Email/password wrong, OR 2FA enabled | If 2FA is on, you can't get a token via login endpoint — use the UI method (Step 1A) |

## Rate limits

By default AcelleMail doesn't impose API rate limits — it trusts authenticated users. If you're hitting the API hard (1000+ calls/sec from a script), consider adding throttling middleware via the [extending source code](https://acellesend.com/kb/articles/extending-acellemail-source-code) guide.

For **public endpoints** (signup forms via `/api/v1/public/subscribers`), DO add throttling — it's the typical spam-bot target.

## What's authenticated as what

A token belongs to **one user**, and inherits that user's permissions:

- **Admin user token:** can access `/customers`, `/subscriptions`, `/sending_servers` admin endpoints + all customer endpoints
- **Customer user token:** can only access endpoints scoped to *their* customer record (their lists, their campaigns, their automations)

This matters for SaaS operators — never give an end customer an admin token; generate them their own.

## Versioning

Current version is `v1`. AcelleMail has held this API stable across multiple major Acelle releases (4.x → 5.x). Breaking changes are rare; additive changes (new endpoints, new fields) are common — always parse responses defensively (`response.field ?? default`).

If a future `/v2/` ships, the announcement will be in [acellemail-updates](https://acellesend.com/kb/category/acellemail-updates) with a migration period for `v1`.

## FAQ

**Can I use OAuth instead of token?** Not natively in current AcelleMail. The token is functionally equivalent for machine-to-machine use cases.

**Is there a Postman collection?** Not officially shipped. Build your own by importing the OpenAPI spec — see the [endpoint reference](https://acellesend.com/kb/articles/rest-api-authentication-and-endpoints) for the resource layout.

**Can a token be revoked?** Yes — generate a new one in the UI (it replaces the old one). The old token immediately becomes invalid.

**How do I expire tokens after N days?** Not built-in; add a custom Laravel scheduled task that nulls `users.api_token` for inactive accounts.

**Webhooks?** Yes, separate from the API — see [Webhook Events Reference](https://acellesend.com/kb/articles/webhook-events-reference) for outbound events (new_subscription, change_plan, etc.).

**Is there a Python / JS SDK?** Not officially. The API surface is small enough that a hand-rolled client per language is straightforward — see Step 4 snippets.

**Can I use this from a browser (CORS)?** AcelleMail doesn't ship CORS headers by default — your `/api/v1/*` calls from a different origin will be blocked. Either proxy through your own backend (recommended) or add CORS headers in nginx for the API routes (see [post-install hardening](https://acellesend.com/kb/articles/post-install-hardening-checklist)).

## Related articles

- [REST API Authentication and Endpoints](https://acellesend.com/kb/articles/rest-api-authentication-and-endpoints) — full endpoint reference (lists, campaigns, subscribers, sending_servers, customers, automations)
- [Webhook Events Reference](https://acellesend.com/kb/articles/webhook-events-reference) — outbound events you can subscribe to
- [Extending AcelleMail Source Code](https://acellesend.com/kb/articles/extending-acellemail-source-code) — when the API isn't enough
- [Zapier Integration Guide](https://acellesend.com/kb/articles/zapier-integration-guide) — no-code automation built on the API
- [Setting Up AcelleMail as a SaaS Platform](https://acellesend.com/kb/articles/setting-up-acellemail-as-a-saas-platform) — per-customer API tokens for SaaS
- [Post-Install Hardening Checklist](https://acellesend.com/kb/articles/post-install-hardening-checklist) — securing the API surface

---

## Related articles

- [Webhook Events Reference](https://acellesend.com/kb/articles/webhook-events-reference): AcelleMail has three distinct webhook systems — customer-lifecycle webhooks (SaaS billing events), per-campaign webhooks (open/click/bounce on a specific campaign), and inbound webhooks from sending providers (SES SNS, Mailgun events). This reference documents each, with the real event names from the Acelle source — not the made-up ones some older guides list.
- [REST API Authentication and Endpoints](https://acellesend.com/kb/articles/rest-api-authentication-and-endpoints): The complete reference for AcelleMail's REST API — all resources, all routes, the per-endpoint request/response shapes, and the scoping rules. Use this alongside the getting-started guide; this article is the lookup table.
- [AcelleMail Webhook Signature Verification and Replay Protection](https://acellesend.com/kb/articles/acellemail-webhook-signature-verification-and-replay-protection): When you receive a webhook from AcelleMail (or AcelleMail receives one from a third-party event source like Meta), the receiver has no built-in trust that the request came from where it claims. This guide walks the HMAC-SHA256 signature verification pattern AcelleMail uses internally for Ads webhook reception, the constant-time-compare hazard most operators get wrong on first attempt, the replay-protection cache pattern, and how to apply the same techniques to your own outbound integrations.
- [Extending AcelleMail Source Code](https://acellesend.com/kb/articles/extending-acellemail-source-code): When the REST API + webhooks aren't enough, AcelleMail's open Laravel codebase lets you extend it directly — add custom fields, integrate a custom sending provider, listen to internal events, override Blade views. This guide walks the upgrade-safe extension patterns, the project structure as it actually is (not what older docs say), and the discipline that keeps your customisations alive through Acelle upgrades.
- [BIMI with VMC — Cost, Process, and Whether It's Worth It](https://acellesend.com/kb/articles/bimi-with-vmc-cost-walkthrough): VMC (Verified Mark Certificate) costs $1,500/yr per domain and takes 4-8 weeks to obtain. This guide walks the cost math, the trademark requirements, the alternative paths, and a decision framework for whether VMC is right for your sending program.
