# 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.

- Canonical URL: https://acellesend.com/kb/articles/rest-api-authentication-and-endpoints
- Author: Tom Becker, Developer Editor
- Category: Developer Guide — REST API, webhooks, customization, extending source code, plugins
- Content type: reference · Difficulty: intermediate
- Tags: Acellemail
- Published: 2026-05-11 · Last updated: 2026-08-15
- Reading time: 10 min
- Publisher: AcelleMail Knowledge Base, https://acellesend.com/kb

---

## What this is for

The complete reference for AcelleMail's REST API. Use this alongside the [Getting Started guide](https://acellesend.com/kb/articles/getting-started-with-the-acellemail-rest-api) — this article is the lookup table.

**Base URL:** `https://mail.example.com/api/v1/`
**Auth:** token (see below) — every authenticated route requires it
**Format:** request `Content-Type: application/json` (POST/PATCH/PUT) + `Accept: application/json` on every request

## Authentication

AcelleMail uses Laravel's `auth:api` driver — a simple bearer-token scheme.

### Get a token

Two methods:

- **UI:** Profile → API Token → Generate / Copy
- **API:** `POST /api/v1/user/login` (unauthenticated):
  ```json
  POST /api/v1/user/login
  email=you@example.com&password=Yours
  → 200 OK
  { "api_token": "..." }
  ```

### Send the token

Either is valid:

| Method | Format | Use when |
|---|---|---|
| Header (preferred) | `Authorization: Bearer <token>` | Production. Doesn't leak to access logs. |
| Query param | `?api_token=<token>` | Quick CLI tests. Avoid in production. |

### Scoping rules

A token is tied to one user, and inherits their scope:

- **Customer user token** → can read/write only resources owned by that customer (their lists, campaigns, subscribers, automations)
- **Admin user token** → can additionally hit admin-only endpoints (`/customers`, `/subscriptions`, `/sending_servers` create/edit)

You cannot impersonate another user. To act as a customer, log in as that customer and use *their* token.

## Conventions

- **UIDs vs IDs:** every resource has both an integer `id` (DB primary key) and a string `uid` (URL-safe, opaque). Routes use `uid` (e.g. `/lists/{uid}/subscribers`). Responses include both.
- **Pagination:** list endpoints return paginated responses with `data`, `current_page`, `per_page`, `total`, `last_page`. Use `?page=2&per_page=50` to navigate (max `per_page` typically 100).
- **Filtering:** most list endpoints support `?filter[field]=value` style filters; specific allowed filters vary per endpoint.
- **Errors:** standard Laravel error responses — `422` for validation, `404` for missing, `401` for auth, `403` for forbidden, `500` for server error. Body includes `message` + (on 422) `errors[]`.
- **Timestamps:** ISO 8601 UTC strings (`2026-05-16T14:32:11.000000Z`).

## Resources

### Public (unauthenticated)

#### Subscribers — public signup form

```
POST /api/v1/public/subscribers
Content-Type: application/json
{
  "MAIL_LIST_UID": "abc123",
  "EMAIL": "alice@example.com",
  "FIRST_NAME": "Alice",
  "LAST_NAME": "Doe",
  ...custom fields per the list schema...
}
→ 200 OK
{ "uid": "subUID", "status": "subscribed" }
```

The only public endpoint. Use it for signup forms on your marketing site — no token needed.

**Add rate limiting at the nginx level** to defend against spam bots (see [post-install hardening](https://acellesend.com/kb/articles/post-install-hardening-checklist)).

### Authentication + identity

| Endpoint | Method | Description |
|---|---|---|
| `/me` | GET | Current authenticated user + their customer |
| `/whoami` | GET | Lighter-weight identity check (id + email only) |
| `/user/login` | POST | Exchange email+password for an api_token (unauthenticated) |
| `/user/info` | GET | Same as `/me` (legacy alias) |
| `/dashboard` | GET | Customer's dashboard summary stats |

### Lists

| Endpoint | Method | Description |
|---|---|---|
| `/lists` | GET | List all subscriber lists owned by current customer |
| `/lists` | POST | Create a new subscriber list |
| `/lists/{uid}` | GET | Get a specific list |
| `/lists/{uid}` | PUT/PATCH | Update list fields |
| `/lists/{uid}` | DELETE | Delete a list (with all its subscribers) |
| `/lists/{uid}/add-field` | POST | Add a custom field to the list |

Create example:

```json
POST /api/v1/lists
{
  "name": "Newsletter Q2",
  "from_email": "news@example.com",
  "from_name": "Example Team",
  "default_subject": "Update from Example",
  "contact": {
    "company": "Example Co",
    "address_1": "123 Main St",
    "city": "San Francisco",
    "country_id": 234,
    "url": "https://example.com"
  }
}
→ 201 Created
{ "uid": "newListUID", "name": "Newsletter Q2", ... }
```

### Subscribers

| Endpoint | Method | Description |
|---|---|---|
| `/subscribers` | GET | List subscribers (paginated) — filter via `?list_uid=` |
| `/subscribers` | POST | Add a new subscriber to a list |
| `/subscribers/{id}` | GET | Get a subscriber by id |
| `/subscribers/{id}` | PATCH | Update subscriber fields |
| `/subscribers/{id}` | DELETE | Delete a subscriber |
| `/subscribers/email/{email}` | GET | Find subscriber by email address |
| `/lists/{list_uid}/subscribers/{id}/subscribe` | PATCH | Mark subscribed |
| `/lists/{list_uid}/subscribers/{id}/unsubscribe` | PATCH | Mark unsubscribed |
| `/lists/{list_uid}/subscribers/email/{email}/unsubscribe` | PATCH | Unsubscribe by email (no id lookup needed) |
| `/subscribers/{id}/add-tag` | POST | Tag a subscriber |
| `/subscribers/{id}/remove-tag` | POST | Untag a subscriber |

Add subscriber:

```json
POST /api/v1/subscribers
{
  "MAIL_LIST_UID": "abc123",
  "EMAIL": "alice@example.com",
  "FIRST_NAME": "Alice"
}
→ 201 Created
{ "uid": "subUID", "email": "alice@example.com", "status": "subscribed" }
```

### Campaigns

| Endpoint | Method | Description |
|---|---|---|
| `/campaigns` | GET | List campaigns (paginated) |
| `/campaigns` | POST | Create a new campaign |
| `/campaigns/{uid}` | GET | Get campaign details + stats |
| `/campaigns/{uid}` | PATCH | Update campaign fields (only allowed before "running") |
| `/campaigns/{uid}` | DELETE | Delete a campaign |
| `/campaigns/{uid}/run` | POST | Start sending the campaign |
| `/campaigns/{uid}/pause` | POST | Pause an in-progress send |
| `/campaigns/{uid}/resume` | POST | Resume a paused campaign |
| `/campaigns/{uid}/tracking-log/download` | GET | Download tracking event log (CSV) |
| `/campaigns/{uid}/open-log/download` | GET | Download open events (CSV) |
| `/campaigns/{uid}/click-log/download` | GET | Download click events (CSV) |
| `/campaigns/{uid}/bounce-log/download` | GET | Download bounce events (CSV) |
| `/campaigns/{uid}/feedback-log/download` | GET | Download spam-complaint events (CSV) |
| `/campaigns/{uid}/unsubscribe-log/download` | GET | Download unsubscribe events (CSV) |

### Automations

| Endpoint | Method | Description |
|---|---|---|
| `/automations` | GET | List automations |
| `/automations/{uid}/api/call` | POST | Trigger an "API trigger" inside an automation (start a subscriber down the flow) |
| `/automations/{uid}/execute` | POST | Manually fire the automation for a given subscriber |

The `api/call` endpoint is what powers the [API trigger feature](https://acellesend.com/kb/articles/using-api-triggers-for-custom-automation-workflows) — your external app POSTs to this URL to begin an automation flow for a specific subscriber.

### Sending servers

| Endpoint | Method | Description |
|---|---|---|
| `/sending_servers` | GET | List sending servers (admin only) |
| `/sending_servers` | POST | Create sending server (admin only) |
| `/sending_servers/{uid}` | GET / PATCH / DELETE | Standard resource ops |

### Customers (admin only)

| Endpoint | Method | Description |
|---|---|---|
| `/customers` | GET | List all customers |
| `/customers` | POST | Create a new customer |
| `/customers/{uid}` | GET / PATCH / DELETE | Standard resource ops |
| `/customers/by-email/{email}` | GET | Find customer by email |
| `/customers/{uid}/disable` | PATCH | Suspend the customer |
| `/customers/{uid}/enable` | PATCH | Reactivate |
| `/customers/{uid}/assign-plan/{plan_uid}` | POST | Assign a plan |
| `/customers/{uid}/change-plan/{plan_uid}` | POST | Change current plan |
| `/customers/{uid}/subscription/update` | POST | Update subscription metadata |
| `/login-token` | GET/POST | Generate a temporary login URL for impersonation (admin debugging) |

### Subscriptions (admin only)

| Endpoint | Method | Description |
|---|---|---|
| `/subscriptions` | GET / POST | List / create subscription records |
| `/subscriptions/{uid}` | GET / PATCH / DELETE | Standard resource ops |

### Notifications (sending-provider webhook inbound)

These endpoints **receive webhooks from the sending provider** (SES SNS, Mailgun, SendGrid event hooks) — you don't typically call them directly:

| Endpoint | Method | Description |
|---|---|---|
| `/notification/bounce` | POST | Sending provider posts bounce events here |
| `/notification/feedback` | POST | Sending provider posts complaint events here |

### File upload + management

| Endpoint | Method | Description |
|---|---|---|
| `/file/upload` | POST | Upload a file (for use in email templates / campaigns) |
| `/filemanager/list` | GET | List uploaded files |
| `/filemanager/upload` | POST | Upload to file manager |
| `/filemanager/delete` | DELETE | Delete uploaded file |
| `/filemanager/rename` | PATCH | Rename |
| `/filemanager/view` | GET | Read file contents |

### System / admin

| Endpoint | Method | Description |
|---|---|---|
| `/plugins/install` | POST | Install a plugin from zip (admin only) |
| `/upgrade/run` | POST | Run an upgrade from a URL |
| `/upgrade/run-file` | POST | Run an upgrade from an uploaded `.bin` patch file |
| `/upgrade/finalize` | POST | Run post-upgrade tasks |
| `/license/refresh` | POST | Re-check the CodeCanyon license |

## Common patterns

### Paginated list

```bash
# Get page 2, 50 per page
curl -H "Authorization: Bearer $TOKEN" \
  "https://mail.example.com/api/v1/subscribers?page=2&per_page=50&filter[list_uid]=abc123"
```

### Create then add many subscribers

```bash
# 1. Create the list
LIST_UID=$(curl -s -X POST ... /api/v1/lists -d '...' | jq -r .uid)

# 2. Bulk-add subscribers (loop or batch via the import flow for > 1000)
for email in $(cat emails.txt); do
  curl -X POST ... /api/v1/subscribers \
    -d "MAIL_LIST_UID=$LIST_UID&EMAIL=$email"
done
```

For > 1000 subscribers, use the [list-import flow](https://acellesend.com/kb/articles/importing-contacts-csv-best-practices-and-field-mapping) — much faster than per-subscriber POST.

### Send a one-off campaign via API

```bash
# 1. Create the campaign
CAMP_UID=$(curl -s -X POST ... /api/v1/campaigns -d '{
  "name": "Q2 Newsletter",
  "type": "regular",
  "subject": "Our June updates",
  "from_email": "news@example.com",
  "from_name": "Example",
  "html_content": "<h1>Hi {FIRST_NAME}</h1>...",
  "mail_list_uid": "abc123",
  "sign_dkim": true,
  "track_open": true,
  "track_click": true
}' | jq -r .uid)

# 2. Start sending
curl -X POST -H "Authorization: Bearer $TOKEN" \
  "https://mail.example.com/api/v1/campaigns/$CAMP_UID/run"
```

### Trigger an automation

```bash
# Customer X just bought a product; start them on the post-purchase flow
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "EMAIL": "buyer@example.com",
    "FIRST_NAME": "Buyer",
    "product_name": "Widget Pro",
    "purchase_amount": 99.00
  }' \
  https://mail.example.com/api/v1/automations/AUTOMATION_UID/api/call
```

The custom fields (`product_name`, `purchase_amount`) are available as merge tags inside the automation's emails.

## Error reference

| HTTP code | Meaning | Common cause |
|---|---|---|
| `200` | OK | Success |
| `201` | Created | Resource created (POST) |
| `204` | No Content | Success with no response body |
| `400` | Bad Request | Malformed JSON, missing required header |
| `401` | Unauthenticated | Missing/expired token |
| `403` | Forbidden | Token valid but lacks permission (e.g. customer trying admin endpoint) |
| `404` | Not Found | UID/email not found, or endpoint typo |
| `415` | Unsupported Media Type | Missing `Accept: application/json` header |
| `422` | Unprocessable Entity | Validation failed; response includes `errors[]` per-field |
| `429` | Too Many Requests | Rate limit hit (if configured) |
| `500` | Internal Server Error | Server bug; check `storage/logs/laravel.log` |

A typical `422` looks like:

```json
{
  "message": "The given data was invalid.",
  "errors": {
    "EMAIL": ["The EMAIL field is required."],
    "MAIL_LIST_UID": ["The selected MAIL_LIST_UID is invalid."]
  }
}
```

## Tips for production use

- **Log every API call's request id** (Laravel emits one per request in the response headers) for traceable debugging
- **Set request timeout** in your client to ≥ 30 seconds — bulk operations (large list creation, campaign run) can take that long
- **Retry on 5xx with exponential backoff**; don't retry on 4xx (it'll just fail again)
- **Never log the token itself** — sanitize before logging headers
- **Rotate the token quarterly** by regenerating in the UI

## FAQ

**Is the API stable across Acelle versions?** v1 has been stable across multiple major releases. Additive changes (new fields, new endpoints) are common; breaking changes are rare and pre-announced in [acellemail-updates](https://acellesend.com/kb/category/acellemail-updates).

**Is there an OpenAPI / Swagger spec?** Not officially shipped. Build by introspecting `routes/api.php` and generating from controller signatures.

**Does the API support GraphQL?** No — REST only.

**Can the API run a campaign for me without an existing list?** No — campaigns must reference a `mail_list_uid` that already exists. Create the list first.

**What's the difference between `id` and `uid`?** `id` is the DB primary key (integer); `uid` is the URL-safe string identifier. Always use `uid` in routes; you'll see both in responses.

**Webhooks?** Different system from the API. See [Webhook Events Reference](https://acellesend.com/kb/articles/webhook-events-reference) for outbound events your app can subscribe to.

## Related articles

- [Getting Started with the AcelleMail REST API](https://acellesend.com/kb/articles/getting-started-with-the-acellemail-rest-api) — the 5-minute kickoff (start here if you haven't)
- [Webhook Events Reference](https://acellesend.com/kb/articles/webhook-events-reference) — outbound events
- [Extending AcelleMail Source Code](https://acellesend.com/kb/articles/extending-acellemail-source-code) — when the API isn't enough
- [Using API Triggers for Custom Automation Workflows](https://acellesend.com/kb/articles/using-api-triggers-for-custom-automation-workflows) — the `/automations/{uid}/api/call` use case
- [Zapier Integration Guide](https://acellesend.com/kb/articles/zapier-integration-guide) — no-code API client
- [Importing Contacts: CSV Best Practices and Field Mapping](https://acellesend.com/kb/articles/importing-contacts-csv-best-practices-and-field-mapping) — for bulk subscriber adds
- [Setting Up AcelleMail as a SaaS Platform](https://acellesend.com/kb/articles/setting-up-acellemail-as-a-saas-platform) — per-customer API tokens
- [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.
- [Getting Started with the AcelleMail REST API](https://acellesend.com/kb/articles/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.
- [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.
