Self-hosted email marketing with full source code. Pay once, own forever. Get AcelleMail — $74 →

REST API · v1

One token. Every endpoint.

A clean, resource-oriented HTTP API for campaigns, lists, subscribers, automations, sending servers, and customer billing. Authenticate with a single token, query JSON, ship in minutes. The same API plugins use is the same API external clients use — no second-class endpoints.

What you can build

SaaS frontend Mobile app CRM sync Automation triggers Reseller dashboards
REQUEST POST /api/v1/campaigns HEADERS Authorization: Bearer ACELLE_TOKEN Content-Type: application/json BODY { "list_uid": "a3b9..." "name": "Welcome" "subject": "Hi friend" "from_name": "Acme" "from_email": "hi@..." "track_open": true "html": "<h1>…" } HTTPS your.acelle.com RESPONSE 200 OK { "campaign": { "uid": "abc123" "name": "Welcome" "subject": "Hi friend" "status": "ready" "track_open": true "created_at": "2026-05-06T…" "links": { "self": "/api/v1…" } } }

WHY THIS API

No surprises. No second-class endpoints.
The same API your plugins use.

Token-authenticated

Every request carries a single bearer token. Generated per user from Profile → API token. Pass via Authorization: Bearer header or ?api_token= query string. Revoke and rotate at any time.

Resource-oriented

Every concept is a resource with predictable verbs. GET /lists returns the collection, POST /lists creates one, PATCH /lists/:uid updates, DELETE /lists/:uid removes. Sub-resources nest naturally: /lists/:uid/subscribers.

Same as plugins use

There is no internal-vs-external split. The endpoints AcelleMail's own UI hits, the endpoints your plugin extends, and the endpoints your external client reads — identical. See the plugin SDK →

Self-hosted endpoint

The base URL is your own server — https://your-acelle.example.com/api/v1/. No vendor proxy, no shared rate limit, no per-request fee. Throughput is whatever your hardware provides.

QUICK START

From zero to first response in four steps.

Each AcelleMail install hosts its own API at /api/v1/. Replace your-acelle.example.com with your installation hostname.

STEP 1

Generate a token

Sign in to your AcelleMail install. Open your profile menu and click API token. Copy the value — it is unique per user and tied to that user's permissions.

# In your shell:
$ export ACELLE_TOKEN=paste-your-token-here
$ export ACELLE_BASE=https://your-acelle.example.com
STEP 2

Verify the token

Hit /me. If the token is valid you get back your user record. If not, you get a 401 — check the token and base URL.

$ curl $ACELLE_BASE/api/v1/me \
    -H "Authorization: Bearer $ACELLE_TOKEN"

{ "id": 1, "email": "you@…", "uid": "…" }
STEP 3

Create a list

Resources are created with POST. The required body fields for a mailing list are name, from_email, from_name, subject, plus a contact block.

$ curl -X POST $ACELLE_BASE/api/v1/lists \
    -H "Authorization: Bearer $ACELLE_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "name":       "Newsletter",
      "from_name":  "Acme",
      "from_email": "hi@acme.com",
      "subject":    "Welcome"
    }'
STEP 4

Add a subscriber

Use the list's UID from step 3. Custom fields are passed as uppercase params (FIRST_NAME, LAST_NAME, …) matching your list's field config.

$ curl -X POST $ACELLE_BASE/api/v1/subscribers \
    -H "Authorization: Bearer $ACELLE_TOKEN" \
    -d list_uid=$LIST_UID \
    -d EMAIL=alice@example.com \
    -d FIRST_NAME=Alice \
    -d tag=newsletter,beta

RESOURCES

Eight resources. Forty-plus endpoints. Every verb you'd expect.

All endpoints below are namespaced under /api/v1/ and require auth:api token authentication unless marked otherwise. Lifecycle actions (run, pause, resume) are surfaced as POST sub-routes. Log downloads stream the underlying CSV.

Authentication

All authenticated endpoints accept the token via the Authorization: Bearer header (preferred) or the ?api_token= query parameter (legacy). Use POST /user/login to exchange email/password for a fresh token from a programmatic client.

POST/user/loginExchange email + password for an API token. Public endpoint.
GET/meReturns the authenticated user's record. Use this to verify a token.
POST/login-tokenGenerate a one-time login URL the user can click to land authenticated.

Lists

Mailing lists are the primary container for subscribers. Each list ships with a default sender identity, custom fields, and double-opt-in / welcome-email settings. Custom fields are added with the add-field sub-route.

GET/listsPaginated index of lists owned by the token's user.
POST/listsCreate a new list. Required: name, from_email, from_name, subject + contact block.
GET/lists/:uidFetch a single list with its fields and stats.
PATCH/lists/:uidUpdate list metadata, sender identity, or opt-in settings.
POST/lists/:uid/add-fieldAdd a custom field. Type is text, number, or datetime.
DELETE/lists/:uidPermanently delete a list and all its subscribers.

Subscribers

Subscribers belong to a list and carry custom field values, tags, and an open/click history. Status is one of subscribed, unsubscribed, or unconfirmed. Tag operations are additive and idempotent.

GET/subscribers?list_uid=…Paginated index. Filter with open=yes|no, click=yes|no.
POST/subscribersCreate. Required: list_uid, EMAIL. Custom fields as uppercase params.
GET/subscribers/:idFetch a single subscriber by id or email.
GET/subscribers/email/:emailFind every list that has a subscriber with this email.
PATCH/subscribers/:idUpdate fields, tags, or status.
POST/subscribers/:id/add-tagAdd comma-separated tags.
POST/subscribers/:id/remove-tagRemove comma-separated tags.
PATCH/lists/:list_uid/subscribers/:id/subscribeMark subscribed.
PATCH/lists/:list_uid/subscribers/:id/unsubscribeMark unsubscribed.
PATCH/lists/:list_uid/subscribers/email/:email/unsubscribeUnsubscribe by email when you don't have the id.
DELETE/subscribers/:idHard delete from the list.

Campaigns

Campaigns are the unit of sending. Create one with HTML content + tracking flags, transition through run / pause / resume, then download tracking, open, click, bounce, feedback, and unsubscribe logs as CSV.

GET/campaignsPaginated index. Pass per_page, page.
POST/campaignsCreate. Required: list_uid, name, subject, from_*, html.
GET/campaigns/:uidFetch a single campaign with stats.
PATCH/campaigns/:uidUpdate content, subject, or tracking flags.
POST/campaigns/:uid/runQueue the campaign for delivery.
POST/campaigns/:uid/pausePause an in-flight campaign.
POST/campaigns/:uid/resumeResume a paused campaign.
GET/campaigns/:uid/tracking-log/downloadStream the tracking CSV.
GET/campaigns/:uid/{open,click,bounce,feedback,unsubscribe}-log/downloadStream the per-event CSV (one route per event type).
DELETE/campaigns/:uidDelete a campaign (only when not in progress).

Automations

Automations are visual flows of triggers, conditions, and actions. The API exposes the read view plus two ways to fire flows from external systems: execute to run an entire automation, or api/call to trigger an "API call" node mid-flow.

GET/automationsIndex of automations the token's user owns.
POST/automations/:uid/executeTrigger an automation for a subscriber. Useful for transactional flows.
POST/automations/:uid/api/callFire an "API call" trigger node mid-flow with custom payload.

Sending servers

Sending servers are the configurable transports (SMTP, Amazon SES, SendGrid, Mailgun, Postmark, custom drivers from plugins). Manage them programmatically when provisioning new tenants or rotating credentials.

GET/sending_serversIndex of configured sending servers.
POST/sending_serversCreate a new sending server. Driver-specific config under settings.
GET/sending_servers/:uidFetch a single sending server.
PATCH/sending_servers/:uidUpdate settings or rotate credentials.
DELETE/sending_servers/:uidRemove a sending server (campaigns referencing it must be re-routed first).

Adding a new transport (Postal MTA, custom HTTP API, …) is a REGISTRY hook — ship it as a plugin instead of patching core.

Customers ADMIN

Customer endpoints are restricted to admin tokens. Use them to provision tenant accounts, manage subscriptions, switch plans, or generate one-time login URLs for support/impersonation.

GET/customersPaginated index of all customers.
POST/customersCreate a new customer + user account.
GET/customers/by-email/:emailLook up a customer by email.
PATCH/customers/:uidUpdate customer profile / contact / quota.
PATCH/customers/:uid/{enable,disable}Suspend or re-activate access without deleting data.
POST/customers/:uid/change-plan/:plan_uidSwitch a customer to a different plan (charges, prorations, sub renewal).
POST/customers/:uid/assign-plan/:plan_uidAssign a plan without billing flow (admin override).
POST/customers/:uid/subscription/updateUpdate an active subscription's parameters.
POST/login-tokenGenerate a one-time login URL for the target customer.

Subscriptions ADMIN

Subscriptions tie a customer to a plan + billing cadence. Use these endpoints when integrating with external billing systems or auditing recurring revenue.

GET/subscriptionsIndex of all active subscriptions.
POST/subscriptionsCreate a subscription record (typically driven by your gateway webhook).
GET/subscriptions/:uidFetch a single subscription with its renewal history.
PATCH/subscriptions/:uidAdjust quota, cadence, or end date.

Files

Upload images and other assets for use in campaign content. The endpoint accepts multipart/form-data and returns a public URL you can reference in HTML body.

POST/file/uploadUpload a file. Returns { "url": "..." }.

Plugins & upgrades ADMIN

Programmatically install plugins from a download URL, run remote core upgrades in a two-step request flow (so step 2 lands on a fresh PHP worker), or refresh the license. Useful for fleet operators managing many AcelleMail tenants.

POST/plugins/installInstall a plugin from a download URL.
POST/upgrade/runBegin a remote core upgrade (downloads + extracts).
POST/upgrade/run-fileApply a single migration step.
POST/upgrade/finalizeFinalize an upgrade. Must be called as a fresh request after run.
POST/license/refreshRefresh CodeCanyon license info (mirrors Admin → License).

WEBHOOKS

Push events out, instead of polling for them.

AcelleMail ships two webhook surfaces. Lifecycle webhooks fire on customer + subscription + automation transitions and are configured globally. Per-recipient tracking pings fire on opens / clicks / unsubscribes and are configured per-campaign. Both retry on failure with exponential backoff.

Lifecycle webhooks

Customer + subscription transitions

Configured once in Admin → Settings → Webhooks. Each webhook is a name + event + outbound HTTP config (URL, method, headers, body template). Six events ship in core:

new_customerparams: customer_id
new_subscriptioncustomer_id, plan_id
change_plancustomer_id, old_plan_id, new_plan_id
cancel_subscriptioncustomer_id, plan_id
terminate_subscriptioncustomer_id, plan_id
automation_webhookautomation_id (fires from API call nodes)

Retry policy is per-webhook: configurable retry_times (default 2) and retry_after_seconds (default 900). Failed deliveries are logged, retried, and surfaced in the admin webhook log.

Plugins can register additional events via the EVENT hook pattern — acelle/ai, for example, fires ai_task_completed from its custom listeners.

Per-recipient tracking

Opens, clicks, unsubscribes

Configured per campaign or per email-template under Tracking → Webhooks. Three event types fire as the recipient interacts with the message:

openrecipient opened the email (tracking pixel)
clickrecipient clicked a tracked link
unsubscriberecipient hit the unsubscribe link

Each ping carries the campaign uid, subscriber email, and the click URL (for click events). Configure as many tracking webhooks as you need per campaign — useful for parallel mirroring to analytics + CRM + data warehouse.

Sending-server-level events (bounce, complaint, delivery) flow through the App\\SendingServers\\Webhooks\\* internal listener stack — surfaced in the campaign log endpoints rather than as outbound webhooks. Download log CSVs →

Example: new_subscription payload

POST /your-webhook-url
Content-Type: application/json
X-AcelleMail-Event: new_subscription
X-AcelleMail-Signature: sha256=…

{
  "event":       "new_subscription",
  "customer_id": 1234,
  "plan_id":     "extended-monthly",
  "fired_at":    "2026-05-06T12:34:56Z"
}

Example: click tracking payload

POST /your-webhook-url
Content-Type: application/json
X-AcelleMail-Event: click

{
  "event":         "click",
  "campaign_uid":  "abc123",
  "subscriber":    "alice@example.com",
  "url":           "https://acme.com/promo",
  "clicked_at":    "2026-05-06T12:34:56Z"
}

RESPONSES & ERRORS

Predictable JSON. Conventional status codes.

Every successful response is JSON. Every failed response is JSON, with a top-level message and a errors map for validation failures. Status codes follow REST convention — you can branch your client on the code alone.

Code When you'll see it
200 OKSuccessful read or update.
201 CreatedSuccessful create.
401 UnauthorizedMissing or invalid token.
403 ForbiddenToken valid, but the resource isn't yours / not admin.
404 Not FoundUID does not exist.
422 UnprocessableValidation error. Check errors map in the body.
500 ServerServer-side bug. Check storage/logs/laravel.log.

Validation error (422)

HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json

{
  "message": "The given data was invalid.",
  "errors": {
    "from_email": [
      "The from_email field is required."
    ],
    "subject": [
      "The subject must be at most 998 characters."
    ]
  }
}

Auth failure (401)

HTTP/1.1 401 Unauthorized
Content-Type: application/json

{ "message": "Unauthenticated." }

Pagination

Index endpoints accept per_page (default 10–25 depending on resource) and page. Responses include the standard Laravel paginator shape: data[], links, meta.current_page, meta.total.

Rate limits

Rate limiting is your call — set it on your Laravel install via RouteServiceProvider. Stock builds ship with no API throttle, since you're hitting your own server. Add throttle:60,1 if you want one.

Versioning

Current major version is v1. New endpoints are added freely; breaking changes to existing endpoints would ship as v2 alongside v1. Pin your client to /api/v1/ and you're stable.

CLIENTS

Any HTTP client. No proprietary SDK.

There is no AcelleMail SDK to install. The API speaks JSON over HTTP — reach for whatever client your stack already has. Three of the most common in practice:

curl · one-liners

curl $ACELLE_BASE/api/v1/lists \
  -H "Authorization: Bearer $ACELLE_TOKEN" \
  | jq '.data[].name'

Laravel HTTP client

$resp = Http::withToken($token)
    ->baseUrl($base.'/api/v1')
    ->post('/subscribers', [
        'list_uid' => $listUid,
        'EMAIL'    => $email,
    ]);

return $resp->json();

Browser / Node fetch

const r = await fetch(
  `${base}/api/v1/campaigns/${uid}/run`,
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
    },
  }
);
const data = await r.json();

Building an open-source SDK? Email us — we'll link it from this page.

CANONICAL REFERENCE

The full, executable API reference.

Every parameter. Every return shape. Every working curl example. Hosted at the canonical demo install — the same code your install runs.

api.acellemail.com

Replace api.acellemail.com with your own install hostname when calling.

START BUILDING

Run AcelleMail. Get a token. Make a call.

One-time license. Full source. Self-hosted endpoint. The API is included — no add-on, no rate plan.

Get AcelleMail — $74 Plugin SDK →