@@ -0,0 +1,4 @@
|
||||
# Documentation
|
||||
|
||||
- Documentation is in `docs` and is built with mkdocs (inside `docs`, run `mkdocs serve` to preview after running `pip install mkdocs-material`).
|
||||
- The OpenAPI (Swagger) spec for the EagleCast API is in `swagger`.
|
||||
@@ -0,0 +1,85 @@
|
||||
# APIs
|
||||
|
||||
All features that are available on the EagleCast dashboard are also available as REST-like HTTP APIs that can be interacted with directly. Request and response bodies are JSON. This allows easy scripting of EagleCast and integration with other systems, for instance, synchronisation with external subscriber databases.
|
||||
|
||||
!!! note
|
||||
If you come across API calls that are yet to be documented, please consider contributing to docs.
|
||||
|
||||
|
||||
## Auth
|
||||
HTTP API requests support BasicAuth and a Authorization `token` headers. API users and tokens with the required permissions can be created and managed on the admin UI (Admin -> Users).
|
||||
|
||||
##### BasicAuth example
|
||||
```shell
|
||||
curl -u "api_user:token" http://localhost:9000/api/lists
|
||||
```
|
||||
|
||||
##### Authorization token example
|
||||
```shell
|
||||
curl -H "Authorization: token api_user:token" http://localhost:9000/api/lists
|
||||
```
|
||||
|
||||
## Permissions
|
||||
**User role**: Permissions allowed for a user are defined as a *User role* (Admin -> User roles) and then attached to a user.
|
||||
|
||||
**List role**: Read / write permissions per-list can be defined as a *List role* (Admin -> User roles) and then attached to a user.
|
||||
|
||||
In a *User role*, `lists:get_all` or `lists:manage_all` permission supercede and override any list specific permissions for a user defined in a *List role*.
|
||||
|
||||
To manage lists and subscriber list subscriptions via API requests, ensure that the appropriate permissions are attached to the API user.
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
## Response structure
|
||||
|
||||
### Successful request
|
||||
|
||||
```http
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
All responses from the API server are JSON with the content-type application/json unless explicitly stated otherwise. A successful 200 OK response always has a JSON response body with a status key with the value success. The data key contains the full response payload.
|
||||
|
||||
### Failed request
|
||||
|
||||
```http
|
||||
HTTP/1.1 500 Server error
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
A failure response is preceded by the corresponding 40x or 50x HTTP header. There may be an optional `data` key with additional payload.
|
||||
|
||||
### Timestamps
|
||||
|
||||
All timestamp fields are in the format `2019-01-01T09:00:00.000000+05:30`. The seconds component is suffixed by the milliseconds, followed by the `+` and the timezone offset.
|
||||
|
||||
### Common HTTP error codes
|
||||
|
||||
| Code | |
|
||||
| ----- | ----------------------------------------------------------------------------|
|
||||
| 400 | Missing or bad request parameters or values |
|
||||
| 403 | Session expired or invalidate. Must relogin |
|
||||
| 404 | Request resource was not found |
|
||||
| 405 | Request method (GET, POST etc.) is not allowed on the requested endpoint |
|
||||
| 410 | The requested resource is gone permanently |
|
||||
| 422 | Unprocessable entity. Unable to process request as it contains invalid data |
|
||||
| 429 | Too many requests to the API (rate limiting) |
|
||||
| 500 | Something unexpected went wrong |
|
||||
| 502 | The backend OMS is down and the API is unable to communicate with it |
|
||||
| 503 | Service unavailable; the API is down |
|
||||
| 504 | Gateway timeout; the API is unreachable |
|
||||
|
||||
|
||||
## OpenAPI (Swagger) spec
|
||||
|
||||
The auto-generated OpenAPI (Swagger) specification site for the APIs are available at **EagleCast.app/docs/swagger**
|
||||
|
||||
|
||||
|
||||
## OpenAPI (Swagger) spec
|
||||
|
||||
The auto-generated OpenAPI (Swagger) specification site for the APIs are available at **EagleCast.app/docs/swagger**
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
# API / Bounces
|
||||
|
||||
Method | Endpoint | Description
|
||||
---------|---------------------------------------------------------|------------------------------------------------
|
||||
GET | [/api/bounces](#get-apibounces) | Retrieve bounce records.
|
||||
DELETE | [/api/bounces](#delete-apibounces) | Delete all/multiple bounce records.
|
||||
DELETE | [/api/bounces/{bounce_id}](#delete-apibouncesbounce_id) | Delete specific bounce record.
|
||||
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### GET /api/bounces
|
||||
|
||||
Retrieve the bounce records.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:-----------|:---------|:---------|:-----------------------------------------------------------------|
|
||||
| campaign_id| number | | Bounce record retrieval for particular campaign id |
|
||||
| page | number | | Page number for pagination. |
|
||||
| per_page | number | | Results per page. Set to 'all' to return all results. |
|
||||
| source | string | | |
|
||||
| order_by | string | | Fields by which bounce records are ordered. Options:"email", "campaign_name", "source", "created_at". |
|
||||
| order | number | | Sorts the result. Allowed values: 'asc','desc' |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X GET 'http://localhost:9000/api/bounces?campaign_id=1&page=1&per_page=2' \
|
||||
-H 'accept: application/json' -H 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--data '{"source":"demo","order_by":"created_at","order":"asc"}'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"results": [
|
||||
{
|
||||
"id": 839971,
|
||||
"type": "hard",
|
||||
"source": "demo",
|
||||
"meta": {
|
||||
"some": "parameter"
|
||||
},
|
||||
"created_at": "2024-08-20T23:54:22.851858Z",
|
||||
"email": "gilles.deleuze@example.app",
|
||||
"subscriber_uuid": "32ca1f3e-1a1d-42e1-af04-df0757f420f3",
|
||||
"subscriber_id": 60,
|
||||
"campaign": {
|
||||
"id": 1,
|
||||
"name": "Test campaign"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 839725,
|
||||
"type": "hard",
|
||||
"source": "demo",
|
||||
"meta": {
|
||||
"some": "parameter"
|
||||
},
|
||||
"created_at": "2024-08-20T22:46:36.393547Z",
|
||||
"email": "gottfried.leibniz@example.app",
|
||||
"subscriber_uuid": "5911d3f4-2346-4bfc-aad2-eb319ab0e879",
|
||||
"subscriber_id": 13,
|
||||
"campaign": {
|
||||
"id": 1,
|
||||
"name": "Test campaign"
|
||||
}
|
||||
}
|
||||
],
|
||||
"query": "",
|
||||
"total": 528,
|
||||
"per_page": 2,
|
||||
"page": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### DELETE /api/bounces
|
||||
|
||||
To delete all bounces.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:--------|:----------|:---------|:-------------------------------------|
|
||||
| all | bool | Yes | Bool to confirm deleting all bounces |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' -X DELETE 'http://localhost:9000/api/bounces?all=true'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### DELETE /api/bounces
|
||||
|
||||
To delete multiple bounce records.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:--------|:----------|:---------|:-------------------------------------|
|
||||
| id | number | Yes | Id's of bounce records to delete. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' -X DELETE 'http://localhost:9000/api/bounces?id=840965&id=840168&id=840879'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### DELETE /api/bounces/{bounce_id}
|
||||
|
||||
To delete specific bounce id.
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' -X DELETE 'http://localhost:9000/api/bounces/840965'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,545 @@
|
||||
# API / Campaigns
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
| :----- | :-------------------------------------------------------------------------- | :---------------------------------------- |
|
||||
| GET | [/api/campaigns](#get-apicampaigns) | Retrieve all campaigns. |
|
||||
| GET | [/api/campaigns/{campaign_id}](#get-apicampaignscampaign_id) | Retrieve a specific campaign. |
|
||||
| GET | [/api/campaigns/{campaign_id}/preview](#get-apicampaignscampaign_idpreview) | Retrieve preview of a campaign. |
|
||||
| GET | [/api/campaigns/running/stats](#get-apicampaignsrunningstats) | Retrieve stats of specified campaigns. |
|
||||
| GET | [/api/campaigns/analytics/{type}](#get-apicampaignsanalyticstype) | Retrieve view counts for a campaign. |
|
||||
| POST | [/api/campaigns](#post-apicampaigns) | Create a new campaign. |
|
||||
| POST | [/api/campaigns/{campaign_id}/test](#post-apicampaignscampaign_idtest) | Test campaign with arbitrary subscribers. |
|
||||
| PUT | [/api/campaigns/{campaign_id}](#put-apicampaignscampaign_id) | Update a campaign. |
|
||||
| PUT | [/api/campaigns/{campaign_id}/status](#put-apicampaignscampaign_idstatus) | Change status of a campaign. |
|
||||
| PUT | [/api/campaigns/{campaign_id}/archive](#put-apicampaignscampaign_idarchive) | Publish campaign to public archive. |
|
||||
| DELETE | [/api/campaigns/{campaign_id}](#delete-apicampaignscampaign_id) | Delete a campaign. |
|
||||
| DELETE | [/api/campaigns](#delete-apicampaigns) | Delete multiple campaigns. |
|
||||
|
||||
____________________________________________________________________________________________________________________________________
|
||||
|
||||
#### GET /api/campaigns
|
||||
|
||||
Retrieve all campaigns.
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X GET 'http://localhost:9000/api/campaigns?page=1&per_page=100'
|
||||
```
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :------- | :------- | :------- | :----------------------------------------------------------------------- |
|
||||
| order | string | | Sorting order: ASC for ascending, DESC for descending. |
|
||||
| order_by | string | | Result sorting field. Options: name, status, created_at, updated_at. |
|
||||
| query | string | | String to filtter by campaign name and subject (fulltext and substring). |
|
||||
| status | []string | | Status to filter campaigns. Repeat in the query for multiple values. |
|
||||
| tags | []string | | Tags to filter campaigns. Repeat in the query for multiple values. |
|
||||
| page | number | | Page number for paginated results. |
|
||||
| per_page | number | | Results per page. Set as 'all' for all results. |
|
||||
| no_body | boolean | | When set to true, returns response without body content. |
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"results": [
|
||||
{
|
||||
"id": 1,
|
||||
"created_at": "2020-03-14T17:36:41.29451+01:00",
|
||||
"updated_at": "2020-03-14T17:36:41.29451+01:00",
|
||||
"views": 0,
|
||||
"clicks": 0,
|
||||
"lists": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Default list"
|
||||
}
|
||||
],
|
||||
"started_at": null,
|
||||
"to_send": 0,
|
||||
"sent": 0,
|
||||
"uuid": "57702beb-6fae-4355-a324-c2fd5b59a549",
|
||||
"type": "regular",
|
||||
"name": "Test campaign",
|
||||
"subject": "Welcome to eaglecast",
|
||||
"from_email": "No Reply <noreply@yoursite.com>",
|
||||
"body": "<h3>Hi {{ .Subscriber.FirstName }}!</h3>\n\t\t\tThis is a test e-mail campaign. Your second name is {{ .Subscriber.LastName }} and you are from {{ .Subscriber.Attribs.city }}.",
|
||||
"body_source": null,
|
||||
"send_at": "2020-03-15T17:36:41.293233+01:00",
|
||||
"status": "draft",
|
||||
"content_type": "richtext",
|
||||
"tags": [
|
||||
"test-campaign"
|
||||
],
|
||||
"template_id": 1,
|
||||
"messenger": "email"
|
||||
}
|
||||
],
|
||||
"query": "",
|
||||
"total": 1,
|
||||
"per_page": 20,
|
||||
"page": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### GET /api/campaigns/{campaign_id}
|
||||
|
||||
Retrieve a specific campaign.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :---------- | :------ | :------- | :------------------------------------------------------- |
|
||||
| campaign_id | number | Yes | Campaign ID. |
|
||||
| no_body | boolean | | When set to true, returns response without body content. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X GET 'http://localhost:9000/api/campaigns/1'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": 1,
|
||||
"created_at": "2020-03-14T17:36:41.29451+01:00",
|
||||
"updated_at": "2020-03-14T17:36:41.29451+01:00",
|
||||
"views": 0,
|
||||
"clicks": 0,
|
||||
"lists": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Default list"
|
||||
}
|
||||
],
|
||||
"started_at": null,
|
||||
"to_send": 0,
|
||||
"sent": 0,
|
||||
"uuid": "57702beb-6fae-4355-a324-c2fd5b59a549",
|
||||
"type": "regular",
|
||||
"name": "Test campaign",
|
||||
"subject": "Welcome to eaglecast",
|
||||
"from_email": "No Reply <noreply@yoursite.com>",
|
||||
"body": "<h3>Hi {{ .Subscriber.FirstName }}!</h3>\n\t\t\tThis is a test e-mail campaign. Your second name is {{ .Subscriber.LastName }} and you are from {{ .Subscriber.Attribs.city }}.",
|
||||
"body_source": null,
|
||||
"send_at": "2020-03-15T17:36:41.293233+01:00",
|
||||
"status": "draft",
|
||||
"content_type": "richtext",
|
||||
"tags": [
|
||||
"test-campaign"
|
||||
],
|
||||
"template_id": 1,
|
||||
"messenger": "email"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### GET /api/campaigns/{campaign_id}/preview
|
||||
|
||||
Preview a specific campaign.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :---------- | :----- | :------- | :---------------------- |
|
||||
| campaign_id | number | Yes | Campaign ID to preview. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X GET 'http://localhost:9000/api/campaigns/1/preview'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```html
|
||||
<h3>Hi John!</h3>
|
||||
This is a test e-mail campaign. Your second name is Doe and you are from Bengaluru.
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### GET /api/campaigns/running/stats
|
||||
|
||||
Retrieve stats of specified campaigns.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :---------- | :----- | :------- | :----------------------------- |
|
||||
| campaign_id | number | Yes | Campaign IDs to get stats for. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X GET 'http://localhost:9000/api/campaigns/running/stats?campaign_id=1'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": []
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### GET /api/campaigns/analytics/{type}
|
||||
|
||||
Retrieve stats of specified campaigns.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :--- | :--------- | :------- | :-------------------------------------------- |
|
||||
| id | number\[\] | Yes | Campaign IDs to get stats for. |
|
||||
| type | string | Yes | Analytics type: views, links, clicks, bounces |
|
||||
| from | string | Yes | Start value of date range. |
|
||||
| to | string | Yes | End value of date range. |
|
||||
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X GET 'http://localhost:9000/api/campaigns/analytics/views?id=1&from=2024-08-04&to=2024-08-12'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"campaign_id": 1,
|
||||
"count": 10,
|
||||
"timestamp": "2024-08-04T00:00:00Z"
|
||||
},
|
||||
{
|
||||
"campaign_id": 1,
|
||||
"count": 14,
|
||||
"timestamp": "2024-08-08T00:00:00Z"
|
||||
},
|
||||
{
|
||||
"campaign_id": 1,
|
||||
"count": 20,
|
||||
"timestamp": "2024-08-09T00:00:00Z"
|
||||
},
|
||||
{
|
||||
"campaign_id": 1,
|
||||
"count": 21,
|
||||
"timestamp": "2024-08-10T00:00:00Z"
|
||||
},
|
||||
{
|
||||
"campaign_id": 1,
|
||||
"count": 21,
|
||||
"timestamp": "2024-08-11T00:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X GET 'http://localhost:9000/api/campaigns/analytics/links?id=1&from=2024-08-04T18%3A30%3A00.624Z&to=2024-08-12T18%3A29%3A00.624Z'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"url": "https://freethebears.org",
|
||||
"count": 294
|
||||
},
|
||||
{
|
||||
"url": "https://calmcode.io",
|
||||
"count": 278
|
||||
},
|
||||
{
|
||||
"url": "https://climate.nasa.gov",
|
||||
"count": 261
|
||||
},
|
||||
{
|
||||
"url": "https://www.storybreathing.com",
|
||||
"count": 260
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### POST /api/campaigns
|
||||
|
||||
Create a new campaign.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :----------- | :--------- | :------- | :--------------------------------------------------------------------------------------------------------------------- |
|
||||
| name | string | Yes | Campaign name. |
|
||||
| subject | string | Yes | Campaign email subject. |
|
||||
| lists | number\[\] | Yes | List IDs to send campaign to. |
|
||||
| from_email | string | | 'From' email in campaign emails. Defaults to value from settings if not provided. |
|
||||
| type | string | Yes | Campaign type: 'regular' or 'optin'. |
|
||||
| content_type | string | Yes | Content type: 'richtext', 'html', 'markdown', 'plain', 'visual'. |
|
||||
| body | string | Yes | Content body of campaign. |
|
||||
| body_source | string | | If content_type is `visual`, the JSON block source of the body. |
|
||||
| altbody | string | | Alternate plain text body for HTML (and richtext) emails. |
|
||||
| send_at | string | | Timestamp to schedule campaign. Format: 'YYYY-MM-DDTHH:MM:SSZ'. |
|
||||
| messenger | string | | 'email' or a custom messenger defined in settings. Defaults to 'email' if not provided. |
|
||||
| template_id | number | | Template ID to use. Defaults to default template if not provided. |
|
||||
| tags | string\[\] | | Tags to mark campaign. |
|
||||
| headers | JSON | | Key-value pairs to send as SMTP headers. Supports template expressions (e.g., `{{ .Subscriber.UUID }}`). Example: \[{"x-custom-header": "value"}, {"x-subscriber": "{{ .Subscriber.UUID }}"}\]. |
|
||||
| attribs | JSON | | Optional JSON object attributes that can be used in the campaign message template. Example `{"location": "Somewhere"}` |
|
||||
|
||||
##### Example request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" 'http://localhost:9000/api/campaigns' -X POST -H 'Content-Type: application/json;charset=utf-8' --data-raw '{"name":"Test campaign","subject":"Hello, world","lists":[1],"from_email":"eaglecast <noreply@eaglecast.yoursite.com>","content_type":"richtext","messenger":"email","type":"regular","tags":["test"],"template_id":1}'
|
||||
```
|
||||
|
||||
##### Example response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": 1,
|
||||
"created_at": "2021-12-27T11:50:23.333485Z",
|
||||
"updated_at": "2021-12-27T11:50:23.333485Z",
|
||||
"views": 0,
|
||||
"clicks": 0,
|
||||
"bounces": 0,
|
||||
"lists": [{
|
||||
"id": 1,
|
||||
"name": "Default list"
|
||||
}],
|
||||
"started_at": null,
|
||||
"to_send": 1,
|
||||
"sent": 0,
|
||||
"uuid": "90c889cc-3728-4064-bbcb-5c1c446633b3",
|
||||
"type": "regular",
|
||||
"name": "Test campaign",
|
||||
"subject": "Hello, world",
|
||||
"from_email": "eaglecast \u003cnoreply@eaglecast.yoursite.com\u003e",
|
||||
"body": "",
|
||||
"body_source": null,
|
||||
"altbody": null,
|
||||
"send_at": null,
|
||||
"status": "draft",
|
||||
"content_type": "richtext",
|
||||
"tags": ["test"],
|
||||
"template_id": 1,
|
||||
"messenger": "email",
|
||||
"headers": {},
|
||||
"attribs": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### POST /api/campaigns/{campaign_id}/test
|
||||
|
||||
Test campaign with arbitrary subscribers.
|
||||
|
||||
Use the same parameters in [POST /api/campaigns](#post-apicampaigns) in addition to the below parameters.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :---------- | :--------- | :------- | :------------------------------------------------- |
|
||||
| subscribers | string\[\] | Yes | List of subscriber e-mails to send the message to. |
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### PUT /api/campaigns/{campaign_id}
|
||||
|
||||
Update a campaign.
|
||||
|
||||
> Refer to parameters from [POST /api/campaigns](#post-apicampaigns)
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### PUT /api/campaigns/{campaign_id}
|
||||
|
||||
Update a specific campaign.
|
||||
|
||||
> Refer to parameters from [POST /api/campaigns](#post-apicampaigns)
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### PUT /api/campaigns/{campaign_id}/status
|
||||
|
||||
Change status of a campaign.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :---------- | :----- | :------- | :---------------------------------------------------------------------- |
|
||||
| campaign_id | number | Yes | Campaign ID to change status. |
|
||||
| status | string | Yes | New status for campaign: 'scheduled', 'running', 'paused', 'cancelled'. |
|
||||
|
||||
##### Note
|
||||
|
||||
> - Only 'scheduled' campaigns can change status to 'draft'.
|
||||
> - Only 'draft' campaigns can change status to 'scheduled'.
|
||||
> - Only 'paused' and 'draft' campaigns can start ('running' status).
|
||||
> - Only 'running' campaigns can change status to 'cancelled' and 'paused'.
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X PUT 'http://localhost:9000/api/campaigns/1/status' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{"status":"scheduled"}'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": 1,
|
||||
"created_at": "2020-03-14T17:36:41.29451+01:00",
|
||||
"updated_at": "2020-04-08T19:35:17.331867+01:00",
|
||||
"views": 0,
|
||||
"clicks": 0,
|
||||
"lists": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Default list"
|
||||
}
|
||||
],
|
||||
"started_at": null,
|
||||
"to_send": 0,
|
||||
"sent": 0,
|
||||
"uuid": "57702beb-6fae-4355-a324-c2fd5b59a549",
|
||||
"type": "regular",
|
||||
"name": "Test campaign",
|
||||
"subject": "Welcome to eaglecast",
|
||||
"from_email": "No Reply <noreply@yoursite.com>",
|
||||
"body": "<h3>Hi {{ .Subscriber.FirstName }}!</h3>\n\t\t\tThis is a test e-mail campaign. Your second name is {{ .Subscriber.LastName }} and you are from {{ .Subscriber.Attribs.city }}.",
|
||||
"send_at": "2020-03-15T17:36:41.293233+01:00",
|
||||
"status": "scheduled",
|
||||
"content_type": "richtext",
|
||||
"tags": [
|
||||
"test-campaign"
|
||||
],
|
||||
"template_id": 1,
|
||||
"messenger": "email"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### PUT /api/campaigns/{campaign_id}/archive
|
||||
|
||||
Publish campaign to public archive.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :------------------ | :---------- | :------- | :------------------------------------------------------------------------ |
|
||||
| campaign_id | number | Yes | Campaign ID to publish to public archive. |
|
||||
| archive | bool | Yes | State of the public archive. |
|
||||
| archive_template_id | number | No | Archive template id. Defaults to 0. |
|
||||
| archive_meta | JSON string | No | Optional Metadata to use in campaign message or template.Eg: name, email. |
|
||||
| archive_slug | string | No | Name for page to be used in public archive URL |
|
||||
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
|
||||
curl -u "api_user:token" -X PUT 'http://localhost:8080/api/campaigns/33/archive'
|
||||
--header 'Content-Type: application/json'
|
||||
--data-raw '{"archive":true,"archive_template_id":1,"archive_meta":{},"archive_slug":"my-newsletter-old-edition"}'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"archive": true,
|
||||
"archive_template_id": 1,
|
||||
"archive_meta": {},
|
||||
"archive_slug": "my-newsletter-old-edition"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### DELETE /api/campaigns/{campaign_id}
|
||||
|
||||
Delete a campaign.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :---------- | :----- | :------- | :--------------------- |
|
||||
| campaign_id | number | Yes | Campaign ID to delete. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X DELETE 'http://localhost:9000/api/campaigns/34'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### DELETE /api/campaigns
|
||||
|
||||
Delete multiple campaigns by IDs or by a search query.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :---- | :--------- | :---------------------------- | :-------------------------------------------------------------------------- |
|
||||
| id | number\[\] | Yes (if `query` not provided) | Onr or more campaign IDs to delete. |
|
||||
| query | string | Yes (if `id` not provided) | Fulltext search query to filter campaigns for deletion (same as GET query). |
|
||||
|
||||
##### Example Request (by IDs)
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X DELETE 'http://localhost:9000/api/campaigns?id=10&id=11&id=12'
|
||||
```
|
||||
|
||||
##### Example Request (by search query)
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X DELETE 'http://localhost:9000/api/campaigns?query=test%20campaign'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,119 @@
|
||||
# API / Import
|
||||
|
||||
Method | Endpoint | Description
|
||||
---------|-------------------------------------------------|------------------------------------------------
|
||||
GET | [/api/import/subscribers](#get-apiimportsubscribers) | Retrieve import statistics.
|
||||
GET | [/api/import/subscribers/logs](#get-apiimportsubscriberslogs) | Retrieve import logs.
|
||||
POST | [/api/import/subscribers](#post-apiimportsubscribers) | Upload a file for bulk subscriber import.
|
||||
DELETE | [/api/import/subscribers](#delete-apiimportsubscribers) | Stop and remove an import.
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### GET /api/import/subscribers
|
||||
|
||||
Retrieve the status of an ongoing import.
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X GET 'http://localhost:9000/api/import/subscribers'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"name": "",
|
||||
"total": 0,
|
||||
"imported": 0,
|
||||
"status": "none"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### GET /api/import/subscribers/logs
|
||||
|
||||
Retrieve logs from an ongoing import.
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X GET 'http://localhost:9000/api/import/subscribers/logs'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": "2020/04/08 21:55:20 processing 'import.csv'\n2020/04/08 21:55:21 imported finished\n"
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### POST /api/import/subscribers
|
||||
|
||||
Send a CSV (optionally ZIP compressed) file to import subscribers. Use a multipart form POST.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:-------|:------------|:---------|:-----------------------------------------|
|
||||
| params | JSON string | Yes | Stringified JSON with import parameters. |
|
||||
| file | file | Yes | File for upload. |
|
||||
|
||||
|
||||
#### `params` (JSON string)
|
||||
| Name | Type | Required | Description |
|
||||
|:----------|:---------|:---------|:-----------------------------------------------------------------------------------------------------------------------------------|
|
||||
| mode | string | Yes | `subscribe` or `blocklist` |
|
||||
| delim | string | Yes | Single character indicating delimiter used in the CSV file, eg: `,` |
|
||||
| lists | []number | | Array of list IDs to subscribe to. |
|
||||
| overwrite | bool | | Whether to overwrite the subscriber parameters including subscriptions or ignore records that are already present in the database. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X POST 'http://localhost:9000/api/import/subscribers' \
|
||||
-F 'params={"mode":"subscribe", "subscription_status":"confirmed", "delim":",", "lists":[1, 2], "overwrite": true}' \
|
||||
-F "file=@/path/to/subs.csv"
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "subscribe", // subscribe or blocklist
|
||||
"delim": ",", // delimiter in the uploaded file
|
||||
"lists":[1], // array of list IDs to import into
|
||||
"overwrite": true // overwrite existing entries or skip them?
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### DELETE /api/import/subscribers
|
||||
|
||||
Stop and delete an ongoing import.
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X DELETE 'http://localhost:9000/api/import/subscribers'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"name": "",
|
||||
"total": 0,
|
||||
"imported": 0,
|
||||
"status": "none"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,296 @@
|
||||
# API / Lists
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
| :----- | :---------------------------------------------- | :------------------------ |
|
||||
| GET | [/api/lists](#get-apilists) | Retrieve all lists. |
|
||||
| GET | [/api/public/lists](#get-public-apilists) | Retrieve public lists. |
|
||||
| GET | [/api/lists/{list_id}](#get-apilistslist_id) | Retrieve a specific list. |
|
||||
| POST | [/api/lists](#post-apilists) | Create a new list. |
|
||||
| PUT | [/api/lists/{list_id}](#put-apilistslist_id) | Update a list. |
|
||||
| DELETE | [/api/lists/{list_id}](#delete-apilistslist_id) | Delete a list. |
|
||||
| DELETE | [/api/lists](#delete-apilists) | Delete multiple lists. |
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### GET /api/lists
|
||||
|
||||
Retrieve lists.
|
||||
|
||||
> **Note:** Lists with `status: archived` are hidden from list selectors in campaigns, public subscription forms, and roles by default. They can only be viewed by filtering with `status=archived` or by viewing all lists without a status filter.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :------- | :------- | :------- | :------------------------------------------------------------------------------------------------- |
|
||||
| query | string | | String for list name search. |
|
||||
| status | string | | Status to filter lists. Options: active, archived. Defaults to showing all lists if not specified. |
|
||||
| minimal | boolean | | If true, returns lists without subscriber counts (faster). Defaults to false. |
|
||||
| tag | []string | | Tags to filter lists. Repeat in the query for multiple values. |
|
||||
| order_by | string | | Sort field. Options: name, status, created_at, updated_at. |
|
||||
| order | string | | Sorting order. Options: ASC, DESC. |
|
||||
| page | number | | Page number for pagination. |
|
||||
| per_page | number | | Results per page. Set to 'all' to return all results. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
# Get all lists
|
||||
curl -u "api_user:token" -X GET 'http://localhost:9000/api/lists?page=1&per_page=100'
|
||||
|
||||
# Get only active lists
|
||||
curl -u "api_user:token" -X GET 'http://localhost:9000/api/lists?status=active&per_page=100'
|
||||
|
||||
# Get archived lists with minimal data
|
||||
curl -u "api_user:token" -X GET 'http://localhost:9000/api/lists?status=archived&minimal=true&per_page=all'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"results": [
|
||||
{
|
||||
"id": 1,
|
||||
"created_at": "2020-02-10T23:07:16.194843+01:00",
|
||||
"updated_at": "2020-03-06T22:32:01.118327+01:00",
|
||||
"uuid": "ce13e971-c2ed-4069-bd0c-240e9a9f56f9",
|
||||
"name": "Default list",
|
||||
"type": "public",
|
||||
"optin": "double",
|
||||
"status": "active",
|
||||
"tags": [
|
||||
"test"
|
||||
],
|
||||
"subscriber_count": 2
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"created_at": "2020-03-04T21:12:09.555013+01:00",
|
||||
"updated_at": "2020-03-06T22:34:46.405031+01:00",
|
||||
"uuid": "f20a2308-dfb5-4420-a56d-ecf0618a102d",
|
||||
"name": "get",
|
||||
"type": "private",
|
||||
"optin": "single",
|
||||
"status": "active",
|
||||
"tags": [],
|
||||
"subscriber_count": 0
|
||||
}
|
||||
],
|
||||
"total": 5,
|
||||
"per_page": 20,
|
||||
"page": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### GET /api/public/lists
|
||||
|
||||
Retrieve public lists with name and uuid to submit a subscription. This is an unauthenticated call to enable scripting to subscription form.
|
||||
|
||||
> **Note:** This endpoint only returns lists with `type: public` and `status: active`. Archived lists are never shown on public subscription forms.
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -X GET 'http://localhost:9000/api/public/lists'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"uuid": "55e243af-80c6-4169-8d7f-bc571e0269e9",
|
||||
"name": "Opt-in list"
|
||||
}
|
||||
]
|
||||
```
|
||||
______________________________________________________________________
|
||||
|
||||
#### GET /api/lists/{list_id}
|
||||
|
||||
Retrieve a specific list.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :------ | :----- | :------- | :-------------------------- |
|
||||
| list_id | number | Yes | ID of the list to retrieve. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X GET 'http://localhost:9000/api/lists/5'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": 5,
|
||||
"created_at": "2020-03-07T06:31:06.072483+01:00",
|
||||
"updated_at": "2020-03-07T06:31:06.072483+01:00",
|
||||
"uuid": "1bb246ab-7417-4cef-bddc-8fc8fc941d3a",
|
||||
"name": "Test list",
|
||||
"type": "public",
|
||||
"optin": "double",
|
||||
"status": "active",
|
||||
"tags": [],
|
||||
"subscriber_count": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### POST /api/lists
|
||||
|
||||
Create a new list.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :---------- | :--------- | :------- | :----------------------------------------------------------------- |
|
||||
| name | string | Yes | Name of the new list. |
|
||||
| type | string | Yes | Type of list. Options: private, public. |
|
||||
| optin | string | Yes | Opt-in type. Options: single, double. |
|
||||
| status | string | No | Status of the list. Options: active, archived. Defaults to active. |
|
||||
| tags | string\[\] | | Associated tags for a list. |
|
||||
| description | string | No | Description of the new list. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X POST 'http://localhost:9000/api/lists'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": 5,
|
||||
"created_at": "2020-03-07T06:31:06.072483+01:00",
|
||||
"updated_at": "2020-03-07T06:31:06.072483+01:00",
|
||||
"uuid": "1bb246ab-7417-4cef-bddc-8fc8fc941d3a",
|
||||
"name": "Test list",
|
||||
"type": "public",
|
||||
"optin": "single",
|
||||
"status": "active",
|
||||
"tags": [],
|
||||
"subscriber_count": 0,
|
||||
"description": "This is a test list"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### PUT /api/lists/{list_id}
|
||||
|
||||
Update a list.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :---------- | :--------- | :------- | :--------------------------------------------- |
|
||||
| list_id | number | Yes | ID of the list to update. |
|
||||
| name | string | | New name for the list. |
|
||||
| type | string | | Type of list. Options: private, public. |
|
||||
| optin | string | | Opt-in type. Options: single, double. |
|
||||
| status | string | | Status of the list. Options: active, archived. |
|
||||
| tags | string\[\] | | Associated tags for the list. |
|
||||
| description | string | | Description of the list. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X PUT 'http://localhost:9000/api/lists/5' \
|
||||
--form 'name=modified test list' \
|
||||
--form 'type=private'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": 5,
|
||||
"created_at": "2020-03-07T06:31:06.072483+01:00",
|
||||
"updated_at": "2020-03-07T06:52:15.208075+01:00",
|
||||
"uuid": "1bb246ab-7417-4cef-bddc-8fc8fc941d3a",
|
||||
"name": "modified test list",
|
||||
"type": "private",
|
||||
"optin": "single",
|
||||
"status": "active",
|
||||
"tags": [],
|
||||
"subscriber_count": 0,
|
||||
"description": "This is a test list"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### DELETE /api/lists/{list_id}
|
||||
|
||||
Delete a specific list.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :------ | :----- | :------- | :------------------------ |
|
||||
| list_id | Number | Yes | ID of the list to delete. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' -X DELETE 'http://localhost:9000/api/lists/1'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### DELETE /api/lists
|
||||
|
||||
Delete multiple lists by IDs or by a search query.
|
||||
|
||||
> **Note:** Users can only delete lists they have `manage` permission for. Any lists in the query that the user doesn't have permission to manage is ignored.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :---- | :--------- | :---------------------------- | :----------------------------------------------------------------- |
|
||||
| id | number\[\] | Yes (if `query` not provided) | One or more list IDs to delete. |
|
||||
| query | string | Yes (if `id` not provided) | Search query to filter lists for deletion (same as the GET query). |
|
||||
|
||||
##### Example Request (by IDs)
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X DELETE 'http://localhost:9000/api/lists?id=10&id=11&id=12'
|
||||
```
|
||||
|
||||
##### Example Request (by search query)
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X DELETE 'http://localhost:9000/api/lists?query=test%20list'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,134 @@
|
||||
# API / Media
|
||||
|
||||
Method | Endpoint | Description
|
||||
-------|------------------------------------------------------|---------------------------------
|
||||
GET | [/api/media](#get-apimedia) | Get uploaded media file
|
||||
GET | [/api/media/{media_id}](#get-apimediamedia_id) | Get specific uploaded media file
|
||||
POST | [/api/media](#post-apimedia) | Upload media file
|
||||
DELETE | [/api/media/{media_id}](#delete-apimediamedia_id) | Delete uploaded media file
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### GET /api/media
|
||||
|
||||
Get an uploaded media file.
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X GET 'http://localhost:9000/api/media' \
|
||||
--header 'Content-Type: multipart/form-data; boundary=--------------------------093715978792575906250298'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"uuid": "ec7b45ce-1408-4e5c-924e-965326a20287",
|
||||
"filename": "Media file",
|
||||
"created_at": "2020-04-08T22:43:45.080058+01:00",
|
||||
"thumb_url": "/uploads/image_thumb.jpg",
|
||||
"uri": "/uploads/image.jpg"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
______________________________________________________________________
|
||||
|
||||
#### GET /api/media/{media_id}
|
||||
|
||||
Retrieve a specific media.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:--------------|:----------|:---------|:-----------------|
|
||||
| media_id | Number | Yes | Media ID. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' 'http://localhost:9000/api/media/7'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data":
|
||||
{
|
||||
"id": 7,
|
||||
"uuid": "62e32e97-d6ca-4441-923f-b62607000dd1",
|
||||
"filename": "ResumeB.pdf",
|
||||
"content_type": "application/pdf",
|
||||
"created_at": "2024-08-06T11:28:53.888257+05:30",
|
||||
"thumb_url": null,
|
||||
"provider": "filesystem",
|
||||
"meta": {},
|
||||
"url": "http://localhost:9000/uploads/ResumeB.pdf"
|
||||
}
|
||||
}
|
||||
```
|
||||
______________________________________________________________________
|
||||
|
||||
#### POST /api/media
|
||||
|
||||
Upload a media file.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|-----------|----------|---------------------|
|
||||
| file | File | Yes | Media file to upload|
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X POST 'http://localhost:9000/api/media' \
|
||||
--header 'Content-Type: multipart/form-data; boundary=--------------------------183679989870526937212428' \
|
||||
--form 'file=@/path/to/image.jpg'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": 1,
|
||||
"uuid": "ec7b45ce-1408-4e5c-924e-965326a20287",
|
||||
"filename": "Media file",
|
||||
"created_at": "2020-04-08T22:43:45.080058+01:00",
|
||||
"thumb_uri": "/uploads/image_thumb.jpg",
|
||||
"uri": "/uploads/image.jpg"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### DELETE /api/media/{media_id}
|
||||
|
||||
Delete an uploaded media file.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|----------|-----------|----------|-------------------------|
|
||||
| media_id | number | Yes | ID of media file to delete |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X DELETE 'http://localhost:9000/api/media/1'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,730 @@
|
||||
# API / Subscribers
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
| ------ | --------------------------------------------------------------------------------------- | ---------------------------------------------- |
|
||||
| GET | [/api/subscribers](#get-apisubscribers) | Query and retrieve subscribers. |
|
||||
| GET | [/api/subscribers/{subscriber_id}](#get-apisubscriberssubscriber_id) | Retrieve a specific subscriber. |
|
||||
| GET | [/api/subscribers/{subscriber_id}/export](#get-apisubscriberssubscriber_idexport) | Export a specific subscriber. |
|
||||
| GET | [/api/subscribers/{subscriber_id}/bounces](#get-apisubscriberssubscriber_idbounces) | Retrieve a subscriber bounce records. |
|
||||
| POST | [/api/subscribers](#post-apisubscribers) | Create a new subscriber. |
|
||||
| POST | [/api/subscribers/{subscriber_id}/optin](#post-apisubscriberssubscriber_idoptin) | Sends optin confirmation email to subscribers. |
|
||||
| POST | [/api/public/subscription](#post-apipublicsubscription) | Create a public subscription. |
|
||||
| PUT | [/api/subscribers/lists](#put-apisubscriberslists) | Modify subscriber list memberships. |
|
||||
| PUT | [/api/subscribers/query/lists](#put-apisubscribersquerylists) | Bulk modify list memberships using SQL/Search queries. |
|
||||
| PUT | [/api/subscribers/{subscriber_id}](#put-apisubscriberssubscriber_id) | Update a specific subscriber. |
|
||||
| PATCH | [/api/subscribers/{subscriber_id}](#patch-apisubscriberssubscriber_id) | Partially update a specific subscriber. |
|
||||
| PUT | [/api/subscribers/{subscriber_id}/blocklist](#put-apisubscriberssubscriber_idblocklist) | Blocklist a specific subscriber. |
|
||||
| PUT | [/api/subscribers/blocklist](#put-apisubscribersblocklist) | Blocklist one or many subscribers. |
|
||||
| PUT | [/api/subscribers/query/blocklist](#put-apisubscribersqueryblocklist) | Blocklist subscribers based on SQL expression. |
|
||||
| DELETE | [/api/subscribers/{subscriber_id}](#delete-apisubscriberssubscriber_id) | Delete a specific subscriber. |
|
||||
| DELETE | [/api/subscribers/{subscriber_id}/bounces](#delete-apisubscriberssubscriber_idbounces) | Delete a specific subscriber's bounce records. |
|
||||
| DELETE | [/api/subscribers](#delete-apisubscribers) | Delete one or more subscribers. |
|
||||
| POST | [/api/subscribers/query/delete](#post-apisubscribersquerydelete) | Delete subscribers based on SQL expression. |
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### GET /api/subscribers
|
||||
|
||||
Retrieve all subscribers.
|
||||
|
||||
##### Query parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :------------------ | :----- | :------- | :-------------------------------------------------------------------- |
|
||||
| query | string | | Subscriber search by SQL expression. |
|
||||
| list_id | int[] | | ID of lists to filter by. Repeat in the query for multiple values. |
|
||||
| subscription_status | string | | Subscription status to filter by if there are one or more `list_id`s. |
|
||||
| order_by | string | | Result sorting field. Options: name, status, created_at, updated_at. |
|
||||
| order | string | | Sorting order: ASC for ascending, DESC for descending. |
|
||||
| page | number | | Page number for paginated results. |
|
||||
| per_page | number | | Results per page. Set as 'all' for all results. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' 'http://localhost:9000/api/subscribers?page=1&per_page=100'
|
||||
```
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' 'http://localhost:9000/api/subscribers?list_id=1&list_id=2&page=1&per_page=100'
|
||||
```
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' -X GET 'http://localhost:9000/api/subscribers' \
|
||||
--url-query 'page=1' \
|
||||
--url-query 'per_page=100' \
|
||||
--url-query "query=subscribers.name LIKE 'Test%' AND subscribers.attribs->>'city' = 'Bengaluru'"
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"results": [
|
||||
{
|
||||
"id": 1,
|
||||
"created_at": "2020-02-10T23:07:16.199433+01:00",
|
||||
"updated_at": "2020-02-10T23:07:16.199433+01:00",
|
||||
"uuid": "ea06b2e7-4b08-4697-bcfc-2a5c6dde8f1c",
|
||||
"email": "john@example.com",
|
||||
"name": "John Doe",
|
||||
"attribs": {
|
||||
"city": "Bengaluru",
|
||||
"good": true,
|
||||
"type": "known"
|
||||
},
|
||||
"status": "enabled",
|
||||
"lists": [
|
||||
{
|
||||
"subscription_status": "unconfirmed",
|
||||
"id": 1,
|
||||
"uuid": "ce13e971-c2ed-4069-bd0c-240e9a9f56f9",
|
||||
"name": "Default list",
|
||||
"type": "public",
|
||||
"tags": [
|
||||
"test"
|
||||
],
|
||||
"created_at": "2020-02-10T23:07:16.194843+01:00",
|
||||
"updated_at": "2020-02-10T23:07:16.194843+01:00"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"created_at": "2020-02-18T21:10:17.218979+01:00",
|
||||
"updated_at": "2020-02-18T21:10:17.218979+01:00",
|
||||
"uuid": "ccf66172-f87f-4509-b7af-e8716f739860",
|
||||
"email": "quadri@example.com",
|
||||
"name": "quadri",
|
||||
"attribs": {},
|
||||
"status": "enabled",
|
||||
"lists": [
|
||||
{
|
||||
"subscription_status": "unconfirmed",
|
||||
"id": 1,
|
||||
"uuid": "ce13e971-c2ed-4069-bd0c-240e9a9f56f9",
|
||||
"name": "Default list",
|
||||
"type": "public",
|
||||
"tags": [
|
||||
"test"
|
||||
],
|
||||
"created_at": "2020-02-10T23:07:16.194843+01:00",
|
||||
"updated_at": "2020-02-10T23:07:16.194843+01:00"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"created_at": "2020-02-19T19:10:49.36636+01:00",
|
||||
"updated_at": "2020-02-19T19:10:49.36636+01:00",
|
||||
"uuid": "5d940585-3cc8-4add-b9c5-76efba3c6edd",
|
||||
"email": "sugar@example.com",
|
||||
"name": "sugar",
|
||||
"attribs": {},
|
||||
"status": "enabled",
|
||||
"lists": []
|
||||
}
|
||||
],
|
||||
"query": "",
|
||||
"total": 3,
|
||||
"per_page": 20,
|
||||
"page": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### GET /api/subscribers/{subscriber_id}
|
||||
|
||||
Retrieve a specific subscriber.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :------------ | :----- | :------- | :--------------- |
|
||||
| subscriber_id | Number | Yes | Subscriber's ID. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' 'http://localhost:9000/api/subscribers/1'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": 1,
|
||||
"created_at": "2020-02-10T23:07:16.199433+01:00",
|
||||
"updated_at": "2020-02-10T23:07:16.199433+01:00",
|
||||
"uuid": "ea06b2e7-4b08-4697-bcfc-2a5c6dde8f1c",
|
||||
"email": "john@example.com",
|
||||
"name": "John Doe",
|
||||
"attribs": {
|
||||
"city": "Bengaluru",
|
||||
"good": true,
|
||||
"type": "known"
|
||||
},
|
||||
"status": "enabled",
|
||||
"lists": [
|
||||
{
|
||||
"subscription_status": "unconfirmed",
|
||||
"id": 1,
|
||||
"uuid": "ce13e971-c2ed-4069-bd0c-240e9a9f56f9",
|
||||
"name": "Default list",
|
||||
"type": "public",
|
||||
"tags": [
|
||||
"test"
|
||||
],
|
||||
"created_at": "2020-02-10T23:07:16.194843+01:00",
|
||||
"updated_at": "2020-02-10T23:07:16.194843+01:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
______________________________________________________________________
|
||||
|
||||
#### GET /api/subscribers/{subscriber_id}/export
|
||||
|
||||
Export a specific subscriber data that gives profile, list subscriptions, campaign views and link clicks information. Names of private lists are replaced with "Private list".
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :------------ | :----- | :------- | :--------------- |
|
||||
| subscriber_id | Number | Yes | Subscriber's ID. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' 'http://localhost:9000/api/subscribers/1/export'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"profile": [
|
||||
{
|
||||
"id": 1,
|
||||
"uuid": "c2cc0b31-b485-4d72-8ce8-b47081beadec",
|
||||
"email": "john@example.com",
|
||||
"name": "John Doe",
|
||||
"attribs": {
|
||||
"city": "Bengaluru",
|
||||
"good": true,
|
||||
"type": "known"
|
||||
},
|
||||
"status": "enabled",
|
||||
"created_at": "2024-07-29T11:01:31.478677+05:30",
|
||||
"updated_at": "2024-07-29T11:01:31.478677+05:30"
|
||||
}
|
||||
],
|
||||
"subscriptions": [
|
||||
{
|
||||
"subscription_status": "unconfirmed",
|
||||
"name": "Private list",
|
||||
"type": "private",
|
||||
"created_at": "2024-07-29T11:01:31.478677+05:30"
|
||||
}
|
||||
],
|
||||
"campaign_views": [],
|
||||
"link_clicks": []
|
||||
}
|
||||
```
|
||||
______________________________________________________________________
|
||||
|
||||
#### GET /api/subscribers/{subscriber_id}/bounces
|
||||
|
||||
Get a specific subscriber bounce records.
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :------------ | :----- | :------- | :--------------- |
|
||||
| subscriber_id | Number | Yes | Subscriber's ID. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' 'http://localhost:9000/api/subscribers/1/bounces'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": 841706,
|
||||
"type": "hard",
|
||||
"source": "demo",
|
||||
"meta": {
|
||||
"some": "parameter"
|
||||
},
|
||||
"created_at": "2024-08-22T09:05:12.862877Z",
|
||||
"email": "thomas.hobbes@example.com",
|
||||
"subscriber_uuid": "137c0d83-8de6-44e2-a55f-d4238ab21969",
|
||||
"subscriber_id": 99,
|
||||
"campaign": {
|
||||
"id": 2,
|
||||
"name": "Welcome to eaglecast"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 841680,
|
||||
"type": "hard",
|
||||
"source": "demo",
|
||||
"meta": {
|
||||
"some": "parameter"
|
||||
},
|
||||
"created_at": "2024-08-19T14:07:53.141917Z",
|
||||
"email": "thomas.hobbes@example.com",
|
||||
"subscriber_uuid": "137c0d83-8de6-44e2-a55f-d4238ab21969",
|
||||
"subscriber_id": 99,
|
||||
"campaign": {
|
||||
"id": 1,
|
||||
"name": "Test campaign"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### POST /api/subscribers
|
||||
|
||||
Create a new subscriber.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:-------------------------|:-----------|:---------|:------------------------------------------------------------------------------------------------------------------------------|
|
||||
| email | string | Yes | Subscriber's email address. |
|
||||
| name | string | Yes | Subscriber's name. |
|
||||
| status | string | Yes | Subscriber's status: `enabled`, `blocklisted`. |
|
||||
| lists | number\[\] | | List of list IDs to subscribe to. |
|
||||
| attribs | JSON | | Optional JSON object attributes for the subscriber that can be used in message templates. Example `{"location": "Somewhere"}` |
|
||||
| preconfirm_subscriptions | bool | | If true, subscriptions are marked as confirmed and no opt-in emails are sent for double opt-in lists. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' 'http://localhost:9000/api/subscribers' -H 'Content-Type: application/json' \
|
||||
--data '{"email":"subscriber@domain.com","name":"The Subscriber","status":"enabled","lists":[1],"attribs":{"city":"Bengaluru","projects":3,"stack":{"languages":["go","python"]}}}'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": 3,
|
||||
"created_at": "2019-07-03T12:17:29.735507+05:30",
|
||||
"updated_at": "2019-07-03T12:17:29.735507+05:30",
|
||||
"uuid": "eb420c55-4cfb-4972-92ba-c93c34ba475d",
|
||||
"email": "subscriber@domain.com",
|
||||
"name": "The Subscriber",
|
||||
"attribs": {
|
||||
"city": "Bengaluru",
|
||||
"projects": 3,
|
||||
"stack": { "languages": ["go", "python"] }
|
||||
},
|
||||
"status": "enabled",
|
||||
"lists": [1]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### POST /api/subscribers/{subscribers_id}/optin
|
||||
|
||||
Sends opt-in confirmation email to subscribers.
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' 'http://localhost:9000/api/subscribers/11/optin' -H 'Content-Type: application/json' \
|
||||
--data {}
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
______________________________________________________________________
|
||||
|
||||
#### POST /api/public/subscription
|
||||
|
||||
Create a public subscription, accepts both form encoded or JSON encoded body.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :--------- | :--------- | :------- | :-------------------------- |
|
||||
| email | string | Yes | Subscriber's email address. |
|
||||
| name | string | | Subscriber's name. |
|
||||
| list_uuids | string\[\] | Yes | List of list UUIDs. |
|
||||
|
||||
##### Example JSON Request
|
||||
|
||||
```shell
|
||||
curl 'http://localhost:9000/api/public/subscription' -H 'Content-Type: application/json' \
|
||||
--data '{"email":"subscriber@domain.com","name":"The Subscriber","list_uuids": ["eb420c55-4cfb-4972-92ba-c93c34ba475d", "0c554cfb-eb42-4972-92ba-c93c34ba475d"]}'
|
||||
```
|
||||
|
||||
##### Example Form Request
|
||||
|
||||
```shell
|
||||
curl -u 'http://localhost:9000/api/public/subscription' \
|
||||
-d 'email=subscriber@domain.com' -d 'name=The Subscriber' -d 'l=eb420c55-4cfb-4972-92ba-c93c34ba475d' -d 'l=0c554cfb-eb42-4972-92ba-c93c34ba475d'
|
||||
```
|
||||
|
||||
Note: For form request, use `l` for multiple lists instead of `lists`.
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### PUT /api/subscribers/lists
|
||||
|
||||
Modify subscriber list memberships.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :-------------- | :--------- | :----------------- | :---------------------------------------------------------------- |
|
||||
| ids | number\[\] | Yes | Array of user IDs to be modified. |
|
||||
| action | string | Yes | Action to be applied: `add`, `remove`, or `unsubscribe`. |
|
||||
| target_list_ids | number\[\] | Yes | Array of list IDs to be modified. |
|
||||
| status | string | Required for `add` | Subscriber status: `confirmed`, `unconfirmed`, or `unsubscribed`. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' -X PUT 'http://localhost:9000/api/subscribers/lists' \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data-raw '{"ids": [1, 2, 3], "action": "add", "target_list_ids": [4, 5, 6], "status": "confirmed"}'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
______________________________________________________________________
|
||||
|
||||
#### PUT /api/subscribers/query/lists
|
||||
|
||||
Modify list memberships for multiple subscribers dynamically using a search query and/or SQL expression.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :------------------ | :--------- | :----------------- | :---------------------------------------------------------------------------------------------- |
|
||||
| action | string | Yes | Action to be applied: `add`, `remove`, or `unsubscribe`. |
|
||||
| target_list_ids | number\[\] | Yes | Array of list IDs that the matching subscribers should be added to or removed from. |
|
||||
| query | string | No | SQL expression to filter subscribers (e.g., `subscribers.email LIKE '%@domain.com'`). |
|
||||
| search | string | No | Free-text search string targeting name, email, or other general text attributes. |
|
||||
| list_ids | number\[\] | No | Optional list IDs to limit the query filter scope (only checks subscribers in these source lists). |
|
||||
| status | string | Required for `add` | Subscription status to set when subscribing: `confirmed`, `unconfirmed`, or `unsubscribed`. |
|
||||
| subscription_status | string | No | Optional subscription status filter to apply to the source lists specified in `list_ids`. |
|
||||
|
||||
##### Example Requests
|
||||
|
||||
###### Subscribing Query Matches to a List
|
||||
```shell
|
||||
curl -u 'api_username:access_token' -X PUT 'http://localhost:9000/api/subscribers/query/lists' \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"query": "subscribers.email LIKE '\''%@domain.com'\''",
|
||||
"action": "add",
|
||||
"target_list_ids": [3],
|
||||
"status": "confirmed"
|
||||
}'
|
||||
|
||||
```
|
||||
|
||||
###### Removing Disqualified Subscribers from a List
|
||||
```shell
|
||||
curl -u 'api_username:access_token' -X PUT 'http://localhost:9000/api/subscribers/query/lists' \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"query": "NOT subscribers.email LIKE '\''%@domain.com'\''",
|
||||
"action": "remove",
|
||||
"target_list_ids": [3]
|
||||
}'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
```json
|
||||
{
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### PUT /api/subscribers/{subscriber_id}
|
||||
|
||||
Update a specific subscriber.
|
||||
|
||||
> Refer to parameters from [POST /api/subscribers](#post-apisubscribers). Note: All parameters must be set, if not, the subscriber will be removed from all previously assigned lists.
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### PATCH /api/subscribers/{subscriber_id}
|
||||
|
||||
Partially update a subscriber. Only fields present in the request body are updated. Unlike PUT, omitting the `lists` field preserves existing list subscriptions. `PUT` treats empty `lists` as a signal to clear all subscriptions.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:-------------------------|:-----------|:---------|:----------------------------------------------------------------------------------------------------------------------------------|
|
||||
| email | string | | Subscriber's email address. Updated only if provided. |
|
||||
| name | string | | Subscriber's name. Updated only if provided. |
|
||||
| status | string | | Subscriber's status: `enabled`, `disabled`, `blocklisted`. Updated only if provided. |
|
||||
| lists | number\[\] | | Array of list IDs. If provided, replaces the subscriber's current list subscriptions. If omitted, existing subscriptions are kept. |
|
||||
| attribs | JSON | | JSON object of subscriber attributes. If provided, merged with existing attributes. |
|
||||
| preconfirm_subscriptions | bool | | If true, subscriptions are marked as confirmed and no opt-in emails are sent for double opt-in lists. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' -X PATCH 'http://localhost:9000/api/subscribers/1' \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data '{"name":"Updated Name"}'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": 1,
|
||||
"created_at": "2020-02-10T23:07:16.199433+01:00",
|
||||
"updated_at": "2020-03-10T16:37:17.228327+01:00",
|
||||
"uuid": "ea06b2e7-4b08-4571-b436-5c5cf31c5cd4",
|
||||
"email": "subscriber@domain.com",
|
||||
"name": "Updated Name",
|
||||
"attribs": {},
|
||||
"status": "enabled",
|
||||
"lists": [{
|
||||
"subscription_status": "unconfirmed",
|
||||
"id": 1,
|
||||
"uuid": "ce13e971-c2ed-4069-bd0c-240e9a9f56f9",
|
||||
"name": "Default list",
|
||||
"type": "public",
|
||||
"optin": "single"
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### PUT /api/subscribers/{subscriber_id}/blocklist
|
||||
|
||||
Blocklist a specific subscriber.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :------------ | :----- | :------- | :--------------- |
|
||||
| subscriber_id | Number | Yes | Subscriber's ID. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' -X PUT 'http://localhost:9000/api/subscribers/9/blocklist'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### PUT /api/subscribers/blocklist
|
||||
|
||||
Blocklist multiple subscriber.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :--- | :----- | :------- | :--------------- |
|
||||
| ids | Number | Yes | Subscriber's ID. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' -X PUT 'http://localhost:8080/api/subscribers/blocklist' -H 'Content-Type: application/json' --data-raw '{"ids":[2,1]}'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### PUT /api/subscribers/query/blocklist
|
||||
|
||||
Blocklist subscribers based on SQL expression.
|
||||
|
||||
> Refer to the [querying and segmentation](../querying-and-segmentation.md#querying-and-segmenting-subscribers) section for more information on how to query subscribers with SQL expressions.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :------- | :------- | :------- | :------------------------------------------- |
|
||||
| query | string | Yes | SQL expression to filter subscribers with. |
|
||||
| list_ids | []number | No | Optional list IDs to limit the filtering to. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' -X POST 'http://localhost:9000/api/subscribers/query/blocklist' \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data-raw '{"query":"subscribers.name LIKE \'John Doe\' AND subscribers.attribs->>'\''city'\'' = '\''Bengaluru'\''"}'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### DELETE /api/subscribers/{subscriber_id}
|
||||
|
||||
Delete a specific subscriber.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :------------ | :----- | :------- | :--------------- |
|
||||
| subscriber_id | Number | Yes | Subscriber's ID. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' -X DELETE 'http://localhost:9000/api/subscribers/9'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### DELETE /api/subscribers/{subscriber_id}/bounces
|
||||
|
||||
Delete a subscriber's bounce records
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :--- | :------------ | :------- | :--------------- |
|
||||
| id | subscriber_id | Yes | Subscriber's ID. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' -X DELETE 'http://localhost:9000/api/subscribers/9/bounces'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### DELETE /api/subscribers
|
||||
|
||||
Delete one or more subscribers.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :--- | :--------- | :------- | :------------------------- |
|
||||
| id | number\[\] | Yes | Array of subscriber's IDs. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' -X DELETE 'http://localhost:9000/api/subscribers?id=10&id=11'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### POST /api/subscribers/query/delete
|
||||
|
||||
Delete subscribers based on SQL expression.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :------- | :------- | :------- | :----------------------------------------------------------------- |
|
||||
| query | string | No | SQL expression to filter subscribers with. |
|
||||
| list_ids | []number | No | Optional list IDs to limit the filtering to. |
|
||||
| all | bool | No | When set to `true`, ignores any query and deletes all subscribers. |
|
||||
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' -X POST 'http://localhost:9000/api/subscribers/query/delete' \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data-raw '{"query":"subscribers.name LIKE \'John Doe\' AND subscribers.attribs->>'\''city'\'' = '\''Bengaluru'\''"}'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,230 @@
|
||||
# API / Templates
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|:-------|:------------------------------------------------------------------------------|:-------------------------------|
|
||||
| GET | [/api/templates](#get-apitemplates) | Retrieve all templates |
|
||||
| GET | [/api/templates/{template_id}](#get-apitemplates-template_id) | Retrieve a template |
|
||||
| GET | [/api/templates/{template_id}/preview](#get-apitemplates-template_id-preview) | Retrieve template HTML preview |
|
||||
| POST | [/api/templates](#post-apitemplates) | Create a template |
|
||||
| POST | /api/templates/preview | Render and preview a template |
|
||||
| PUT | [/api/templates/{template_id}](#put-apitemplatestemplate_id) | Update a template |
|
||||
| PUT | [/api/templates/{template_id}/default](#put-apitemplates-template_id-default) | Set default template |
|
||||
| DELETE | [/api/templates/{template_id}](#delete-apitemplates-template_id) | Delete a template |
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### GET /api/templates
|
||||
|
||||
Retrieve all templates.
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X GET 'http://localhost:9000/api/templates'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"created_at": "2020-03-14T17:36:41.288578+01:00",
|
||||
"updated_at": "2020-03-14T17:36:41.288578+01:00",
|
||||
"name": "Default template",
|
||||
"body": "{{ template \"content\" . }}",
|
||||
"body_source": null,
|
||||
"type": "campaign",
|
||||
"is_default": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### GET /api/templates/{template_id}
|
||||
|
||||
Retrieve a specific template.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:------------|:----------|:---------|:-------------------------------|
|
||||
| template_id | number | Yes | ID of the template to retrieve |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X GET 'http://localhost:9000/api/templates/1'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": 1,
|
||||
"created_at": "2020-03-14T17:36:41.288578+01:00",
|
||||
"updated_at": "2020-03-14T17:36:41.288578+01:00",
|
||||
"name": "Default template",
|
||||
"body": "{{ template \"content\" . }}",
|
||||
"body_source": null,
|
||||
"type": "campaign",
|
||||
"is_default": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### GET /api/templates/{template_id}/preview
|
||||
|
||||
Retrieve the HTML preview of a template.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:------------|:----------|:---------|:------------------------------|
|
||||
| template_id | number | Yes | ID of the template to preview |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X GET 'http://localhost:9000/api/templates/1/preview'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```html
|
||||
<p>Hi there</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis et elit ac elit sollicitudin condimentum non a magna.
|
||||
Sed tempor mauris in facilisis vehicula. Aenean nisl urna, accumsan ac tincidunt vitae, interdum cursus massa.
|
||||
Interdum et malesuada fames ac ante ipsum primis in faucibus. Aliquam varius turpis et turpis lacinia placerat.
|
||||
Aenean id ligula a orci lacinia blandit at eu felis. Phasellus vel lobortis lacus. Suspendisse leo elit, luctus sed
|
||||
erat ut, venenatis fermentum ipsum. Donec bibendum neque quis.</p>
|
||||
|
||||
<h3>Sub heading</h3>
|
||||
<p>Nam luctus dui non placerat mattis. Morbi non accumsan orci, vel interdum urna. Duis faucibus id nunc ut euismod.
|
||||
Curabitur et eros id erat feugiat fringilla in eget neque. Aliquam accumsan cursus eros sed faucibus.</p>
|
||||
|
||||
<p>Here is a link to <a href="" target="_blank">eaglecast</a>.</p>
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### POST /api/templates
|
||||
|
||||
Create a template.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:------------|:-------|:---------|:------------------------------------------------------------------------------|
|
||||
| name | string | Yes | Name of the template |
|
||||
| type | string | Yes | Type of the template (`campaign`, `campaign_visual`, or `tx`) |
|
||||
| subject | string | | Subject line for the template (only for `tx`) |
|
||||
| body_source | string | | If type is `campaign_visual`, the JSON source for the email-builder tempalate |
|
||||
| body | string | Yes | HTML body of the template |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X POST 'http://localhost:9000/api/templates' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"name": "New template",
|
||||
"type": "campaign",
|
||||
"subject": "Your Weekly Newsletter",
|
||||
"body": "<h1>Header</h1><p>Content goes here</p>"
|
||||
}'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"created_at": "2020-03-14T17:36:41.288578+01:00",
|
||||
"updated_at": "2020-03-14T17:36:41.288578+01:00",
|
||||
"name": "Default template",
|
||||
"body": "{{ template \"content\" . }}",
|
||||
"body_source": null,
|
||||
"type": "campaign",
|
||||
"is_default": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### PUT /api/templates/{template_id}
|
||||
|
||||
Update a template.
|
||||
|
||||
> Refer to parameters from [POST /api/templates](#post-apitemplates)
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### PUT /api/templates/{template_id}/default
|
||||
|
||||
Set a template as the default.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:------------|:----------|:---------|:-------------------------------------|
|
||||
| template_id | number | Yes | ID of the template to set as default |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X PUT 'http://localhost:9000/api/templates/1/default'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": 1,
|
||||
"created_at": "2020-03-14T17:36:41.288578+01:00",
|
||||
"updated_at": "2020-03-14T17:36:41.288578+01:00",
|
||||
"name": "Default template",
|
||||
"body": "{{ template \"content\" . }}",
|
||||
"body_source": null,
|
||||
"type": "campaign",
|
||||
"is_default": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### DELETE /api/templates/{template_id}
|
||||
|
||||
Delete a template.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:------------|:----------|:---------|:-----------------------------|
|
||||
| template_id | number | Yes | ID of the template to delete |
|
||||
|
||||
##### Example Request
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" -X DELETE 'http://localhost:9000/api/templates/35'
|
||||
```
|
||||
|
||||
##### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,98 @@
|
||||
# API / Transactional
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
| :----- | :------- | :-------------------------- |
|
||||
| POST | /api/tx | Send transactional messages |
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### POST /api/tx
|
||||
|
||||
Allows sending transactional messages to one or more subscribers via a preconfigured transactional template.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| :---------------- | :--------- | :------- | :------------------------------------------------------------------------- |
|
||||
| subscriber_email | string | | Email of the subscriber. Can substitute with `subscriber_id`. |
|
||||
| subscriber_id | number | | Subscriber's ID can substitute with `subscriber_email`. |
|
||||
| subscriber_emails | string\[\] | | Multiple subscriber emails as alternative to `subscriber_email`. |
|
||||
| subscriber_ids | number\[\] | | Multiple subscriber IDs as an alternative to `subscriber_id`. |
|
||||
| subscriber_mode | string | | Subscriber lookup mode: `default`, `fallback`, or `external` |
|
||||
| template_id | number | Yes | ID of the transactional template to be used for the message. |
|
||||
| from_email | string | | Optional sender email. |
|
||||
| subject | string | | Optional subject. If empty, the subject defined on the template is used |
|
||||
| data | JSON | | Optional nested JSON map. Available in the template as `{{ .Tx.Data.* }}`. |
|
||||
| headers | JSON\[\] | | Optional array of email headers. |
|
||||
| messenger | string | | Messenger to send the message. Default is `email`. |
|
||||
| content_type | string | | Email format options include `html`, `markdown`, and `plain`. |
|
||||
| altbody | string | | Optional alternate plaintext body for multipart HTML emails. |
|
||||
|
||||
##### Subscriber modes
|
||||
|
||||
The `subscriber_mode` parameter controls how the recipients (subscribers or non-subscriber recipients) are resolved.
|
||||
|
||||
| Mode | Description |
|
||||
| :--------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `default` | Recipients must exist as subscribers in the database. Pass either `subscriber_emails` or `subscriber_ids`. |
|
||||
| `fallback` | Only accepts `subscriber_emails` and looks up subscribers in the database. If not found, sends the message to the e-mail anyway. In the template, apart from `{{ .Subscriber.Email }}`, other subscriber fields such as `.Name`. will be empty. Use `{{ Tx.Data.* }}` instead. |
|
||||
| `external` | Sends to the given `subscriber_emails` without subscriber lookup in the database. In the template, apart from `{{ .Subscriber.Email }}`, other subscriber fields such as `.Name`. will be empty. Use `{{ Tx.Data.* }}` instead. |
|
||||
|
||||
##### Example
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" "http://localhost:9000/api/tx" -X POST \
|
||||
-H 'Content-Type: application/json; charset=utf-8' \
|
||||
--data-binary @- << EOF
|
||||
{
|
||||
"subscriber_email": "user@test.com",
|
||||
"template_id": 2,
|
||||
"data": {"order_id": "1234", "date": "2022-07-30", "items": [1, 2, 3]},
|
||||
"content_type": "html"
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
##### Example response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
|
||||
##### Example with external mode
|
||||
|
||||
Send to arbitrary email addresses without requiring them to be subscribers:
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" "http://localhost:9000/api/tx" -X POST \
|
||||
-H 'Content-Type: application/json; charset=utf-8' \
|
||||
--data-binary @- << EOF
|
||||
{
|
||||
"subscriber_mode": "external",
|
||||
"subscriber_emails": ["recipient@example.com"],
|
||||
"template_id": 2,
|
||||
"data": {"name": "John", "order_id": "1234"},
|
||||
"content_type": "html"
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
In the template, use `{{ .Tx.Data.name }}`, `{{ .Tx.Data.order_id }}`, etc. to access the data.
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
#### File Attachments
|
||||
|
||||
To include file attachments in a transactional message, use the `multipart/form-data` Content-Type. Use `data` param for the parameters described above as a JSON object. Include any number of attachments via the `file` param.
|
||||
|
||||
```shell
|
||||
curl -u "api_user:token" "http://localhost:9000/api/tx" -X POST \
|
||||
-F 'data=\"{
|
||||
\"subscriber_email\": \"user@test.com\",
|
||||
\"template_id\": 4
|
||||
}"' \
|
||||
-F 'file=@"/path/to/attachment.pdf"' \
|
||||
-F 'file=@"/path/to/attachment2.pdf"'
|
||||
```
|
||||
@@ -0,0 +1,31 @@
|
||||
# Archives
|
||||
|
||||
A global public archive is maintained on the public web interface. It can be
|
||||
enabled under Settings -> Settings -> General -> Enable public mailing list
|
||||
archive.
|
||||
|
||||
To make a campaign available in the public archive (provided it has been
|
||||
enabled in the settings as described above), enable the option
|
||||
'Publish to public archive' under Campaigns -> Create new -> Archive.
|
||||
|
||||
When using template variables that depend on subscriber data (such as any
|
||||
template variable referencing `.Subscriber`), such data must be supplied
|
||||
as 'Campaign metadata', which is a JSON object that will be used in place
|
||||
of `.Subscriber` when rendering the archive template and content.
|
||||
|
||||
When individual subscriber tracking is enabled, TrackLink requires that a UUID
|
||||
of an existing user is provided as part of the campaign metadata. Any clicks on
|
||||
a TrackLink from the archived campaign will be counted towards that subscriber.
|
||||
|
||||
As an example:
|
||||
|
||||
```json
|
||||
{
|
||||
"UUID": "5a837423-a186-5623-9a87-82691cbe3631",
|
||||
"email": "example@example.com",
|
||||
"name": "Reader",
|
||||
"attribs": {}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
# Bounce processing
|
||||
|
||||
Enable bounce processing in Settings -> Bounces. POP3 bounce scanning and APIs only become available once the setting is enabled.
|
||||
|
||||
## POP3 bounce mailbox
|
||||
Configure the bounce mailbox in Settings -> Bounces. Either the "From" e-mail that is set on a campaign (or in settings) should have a POP3 mailbox behind it to receive bounce e-mails, or you should configure a dedicated POP3 mailbox and add that address as the `Return-Path` (envelope sender) header in Settings -> SMTP -> Custom headers box. For example:
|
||||
|
||||
```
|
||||
[
|
||||
{"Return-Path": "your-bounce-inbox@site.com"}
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
Some mail servers may also return the bounce to the `Reply-To` address, which can also be added to the header settings.
|
||||
|
||||
### Bounce classification
|
||||
EagleCast applies a series of heuristics looking for keywords in the bounced mail body to guess if it is a 'soft' bounce or a 'hard' bounce. For instance, 4.x.x and 5.x.x error status codes, common strings such as "mailbox not found" etc. If none of the heuristics match, then the bounce mail is considered to be 'soft' by default.
|
||||
|
||||
## Webhook API
|
||||
The bounce webhook API can be used to record bounce events with custom scripting. This could be by reading a mailbox, a database, or mail server logs.
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
| ------ | ---------------- | ---------------------- |
|
||||
| `POST` | /webhooks/bounce | Record a bounce event. |
|
||||
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| --------------- | ------ | -------- | ------------------------------------------------------------------------------------ |
|
||||
| subscriber_uuid | string | | The UUID of the subscriber. Either this or `email` is required. |
|
||||
| email | string | | The e-mail of the subscriber. Either this or `subscriber_uuid` is required. |
|
||||
| campaign_uuid | string | | UUID of the campaign for which the bounce happened. |
|
||||
| source | string | Yes | A string indicating the source, eg: `api`, `my_script` etc. |
|
||||
| type | string | Yes | `hard` or `soft` bounce. Currently, this has no effect on how the bounce is treated. |
|
||||
| meta | string | | An optional escaped JSON string with arbitrary metadata about the bounce event. |
|
||||
|
||||
|
||||
```shell
|
||||
curl -u 'api_username:access_token' -X POST 'http://localhost:9000/webhooks/bounce' \
|
||||
-H "Content-Type: application/json" \
|
||||
--data '{"email": "user1@mail.com", "campaign_uuid": "9f86b50d-5711-41c8-ab03-bc91c43d711b", "source": "api", "type": "hard", "meta": "{\"additional\": \"info\"}}'
|
||||
|
||||
```
|
||||
|
||||
## External webhooks
|
||||
EagleCast supports receiving bounce webhook events from the following SMTP providers.
|
||||
|
||||
| Endpoint | Description | More info |
|
||||
|:--------------------------------------------------------------|:---------------------------------------|:----------------------------------------------------------------------------------------------------------------------|
|
||||
| `https://eaglecast.yoursite.com/webhooks/service/ses` | Amazon (AWS) SES | See below |
|
||||
| `https://eaglecast.yoursite.com/webhooks/service/azure` | Azure Communication Services (ACS) | [More info](https://learn.microsoft.com/en-us/azure/event-grid/communication-services-email-events) |
|
||||
| `https://eaglecast.yoursite.com/webhooks/service/sendgrid` | Sendgrid / Twilio Signed event webhook | [More info](https://docs.sendgrid.com/for-developers/tracking-events/getting-started-event-webhook-security-features) |
|
||||
| `https://eaglecast.yoursite.com/webhooks/service/postmark` | Postmark webhook | [More info](https://postmarkapp.com/developer/webhooks/webhooks-overview) |
|
||||
| `https://eaglecast.yoursite.com/webhooks/service/forwardemail` | Forward Email webhook | [More info](https://forwardemail.net/en/faq#do-you-support-bounce-webhooks) |
|
||||
| `https://eaglecast.yoursite.com/webhooks/service/lettermint` | Lettermint webhook | [More info](https://lettermint.co/knowledge-base/guides/send-newsletter-with-EagleCast) |
|
||||
|
||||
## Amazon Simple Email Service (SES)
|
||||
|
||||
If using SES as your SMTP provider, automatic bounce processing is the recommended way to maintain your [sender reputation](https://docs.aws.amazon.com/ses/latest/dg/monitor-sender-reputation.html). The settings below are based on Amazon's [recommendations](https://docs.aws.amazon.com/ses/latest/dg/send-email-concepts-deliverability.html). Please note that your sending domain must be verified in SES before proceeding.
|
||||
|
||||
1. In EagleCast settings, go to the "Bounces" tab and configure the following:
|
||||
- Enable bounce processing: `Enabled`
|
||||
- Soft:
|
||||
- Bounce count: `2`
|
||||
- Action: `None`
|
||||
- Hard:
|
||||
- Bounce count: `1`
|
||||
- Action: `Blocklist`
|
||||
- Complaint:
|
||||
- Bounce count: `1`
|
||||
- Action: `Blocklist`
|
||||
- Enable bounce webhooks: `Enabled`
|
||||
- Enable SES: `Enabled`
|
||||
2. In the AWS console, go to [Simple Notification Service](https://console.aws.amazon.com/sns/) and create a new topic with the following settings:
|
||||
- Type: `Standard`
|
||||
- Name: `ses-bounces` (or any other name)
|
||||
3. Create a new subscription to that topic with the following settings:
|
||||
- Protocol: `HTTPS`
|
||||
- Endpoint: `https://eaglecast.yoursite.com/webhooks/service/ses`
|
||||
- Enable raw message delivery: `Disabled` (unchecked)
|
||||
4. SES will then make a request to your EagleCast instance to confirm the subscription. After a page refresh, the subscription should have a status of "Confirmed". If not, your endpoint may be incorrect or not publicly accessible.
|
||||
5. In the AWS console, go to [Simple Email Service](https://console.aws.amazon.com/ses/) and click "Identities" in the left sidebar.
|
||||
6. Click your domain and go to the "Notifications" tab.
|
||||
7. Next to "Feedback notifications", click "Edit".
|
||||
8. For both "Bounce feedback" and "Complaint feedback", use the following settings:
|
||||
- SNS topic: `ses-bounces` (or whatever you named it)
|
||||
- Include original email headers: `Enabled` (checked)
|
||||
9. Repeat steps 6-8 for any `Email address` identities you send from using EagleCast
|
||||
10. Bounce processing should now be working. You can test it with [SES simulator addresses](https://docs.aws.amazon.com/ses/latest/dg/send-an-email-from-console.html#send-email-simulator). Add them as subscribers, send them campaign previews, and ensure that the appropriate action was taken after the configured bounce count was reached.
|
||||
- Soft bounce: `ooto@simulator.amazonses.com`
|
||||
- Hard bounce: `bounce@simulator.amazonses.com`
|
||||
- Complaint: `complaint@simulator.amazonses.com`
|
||||
11. You can optionally [disable email feedback forwarding](https://docs.aws.amazon.com/ses/latest/dg/monitor-sending-activity-using-notifications-email.html#monitor-sending-activity-using-notifications-email-disabling).
|
||||
|
||||
## Azure Communication Services (ACS)
|
||||
|
||||
If you use Azure Communication Services Email, EagleCast can receive delivery report events from Azure Event Grid and turn them into bounces.
|
||||
|
||||
1. In EagleCast settings, go to "Bounces" and configure:
|
||||
- Enable bounce processing: `Enabled`
|
||||
- Enable bounce webhooks: `Enabled`
|
||||
- Enable Azure ACS: `Enabled`
|
||||
- Optional: set `Azure Event Grid Shared Secret`.
|
||||
- Optional: set `Azure Shared Secret Header Name` if you want eaglecast to read the secret from a header (defaults to `X-EagleCast-Webhook-Secret`).
|
||||
2. In EagleCast settings, go to "SMTP" and use the `Azure ACS` quick preset to fill SMTP defaults.
|
||||
3. In Azure, create an Event Grid subscription for your ACS Email events with:
|
||||
- Endpoint type: `Web Hook`
|
||||
- Endpoint URL: `https://eaglecast.yoursite.com/webhooks/service/azure`
|
||||
- If using query-param auth, append `?code=<your-shared-secret>`.
|
||||
- If using header auth, configure Event Grid to include the same secret in the header name configured in eaglecast.
|
||||
4. During subscription creation, Event Grid sends a subscription validation event. EagleCast automatically returns `validationResponse` for this handshake.
|
||||
5. Subscribe to `Microsoft.Communication.EmailDeliveryReportReceived` events. EagleCast maps relevant statuses to bounce records.
|
||||
6. Send test mail and verify bounces in EagleCast.
|
||||
|
||||
## Exporting bounces
|
||||
|
||||
Bounces can be exported via the JSON API:
|
||||
```shell
|
||||
curl -u 'username:passsword' 'http://localhost:9000/api/bounces'
|
||||
```
|
||||
|
||||
Or by querying the database directly:
|
||||
```sql
|
||||
SELECT bounces.created_at,
|
||||
bounces.subscriber_id,
|
||||
subscribers.uuid AS subscriber_uuid,
|
||||
subscribers.email AS email
|
||||
FROM bounces
|
||||
LEFT JOIN subscribers ON (subscribers.id = bounces.subscriber_id)
|
||||
ORDER BY bounces.created_at DESC LIMIT 1000;
|
||||
```
|
||||
@@ -0,0 +1,71 @@
|
||||
# Concepts
|
||||
|
||||
## Subscriber
|
||||
|
||||
A subscriber is a recipient identified by an e-mail address and name. Subscribers receive e-mails that are sent from EagleCast. A subscriber can be added to any number of lists. Subscribers who are not a part of any lists are considered *orphan* records.
|
||||
|
||||
### Attributes
|
||||
|
||||
Attributes are arbitrary properties attached to a subscriber in addition to their e-mail and name. They are represented as a JSON map. It is not necessary for all subscribers to have the same attributes. Subscribers can be [queried and segmented](querying-and-segmentation.md) into lists based on their attributes, and the attributes can be inserted into the e-mails sent to them. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"city": "Bengaluru",
|
||||
"likes_tea": true,
|
||||
"spoken_languages": ["English", "Malayalam"],
|
||||
"projects": 3,
|
||||
"stack": {
|
||||
"frameworks": ["echo", "go"],
|
||||
"languages": ["go", "python"],
|
||||
"preferred_language": "go"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Subscription statuses
|
||||
|
||||
A subscriber can be added to one or more lists, and each such relationship can have one of these statuses.
|
||||
|
||||
| Status | Description |
|
||||
|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `unconfirmed` | The subscriber was added to the list directly without their explicit confirmation. Nonetheless, the subscriber will receive campaign messages sent to single opt-in campaigns. |
|
||||
| `confirmed` | The subscriber confirmed their subscription by clicking on 'accept' in the confirmation e-mail. Only confirmed subscribers in opt-in lists will receive campaign messages send to the list. |
|
||||
| `unsubscribed` | The subscriber is unsubscribed from the list and will not receive any campaign messages sent to the list. |
|
||||
|
||||
### Segmentation
|
||||
|
||||
Segmentation is the process of filtering a large list of subscribers into a smaller group based on arbitrary conditions, primarily based on their attributes. For instance, if an e-mail needs to be sent subscribers who live in a particular city, given their city is described in their attributes, it's possible to quickly filter them out into a new list and e-mail them. [Learn more](querying-and-segmentation.md).
|
||||
|
||||
## List
|
||||
|
||||
A list (or a _mailing list_) is a collection of subscribers grouped under a name, for instance, _clients_. Lists are used to organise subscribers and send e-mails to specific groups. A list can be single opt-in or double opt-in. Subscribers added to double opt-in lists have to explicitly accept the subscription by clicking on the confirmation e-mail they receive. Until then, they do not receive campaign messages.
|
||||
|
||||
## Campaign
|
||||
|
||||
A campaign is an e-mail (or any other kind of messages) that is sent to one or more lists.
|
||||
|
||||
|
||||
## Transactional message
|
||||
|
||||
A transactional message is an arbitrary message sent to a subscriber using the transactional message API. For example a welcome e-mail on signing up to a service; an order confirmation e-mail on purchasing an item; a password reset e-mail when a user initiates an online account recovery process.
|
||||
|
||||
|
||||
## Template
|
||||
|
||||
A template is a re-usable HTML design that can be used across campaigns and when sending arbitrary transactional messages. Most commonly, templates have standard header and footer areas with logos and branding elements, where campaign content is inserted in the middle. EagleCast supports [Go template](https://gowebexamples.com/templates/) expressions that lets you create powerful, dynamic HTML templates. [Learn more](templating.md).
|
||||
|
||||
## Messenger
|
||||
|
||||
EagleCast supports multiple custom messaging backends in additional to the default SMTP e-mail backend, enabling not just e-mail campaigns, but arbitrary message campaigns such as SMS, FCM notifications etc. A *Messenger* is a web service that accepts a campaign message pushed to it as a JSON request, which the service can in turn broadcast as SMS, FCM etc. [Learn more](messengers.md).
|
||||
|
||||
## Tracking pixel
|
||||
|
||||
The tracking pixel is a tiny, invisible image that is inserted into an e-mail body to track e-mail views. This allows measuring the read rate of e-mails. While this is exceedingly common in e-mail campaigns, it carries privacy implications and should be used in compliance with rules and regulations such as GDPR. It is possible to track reads anonymously without associating an e-mail read to a subscriber.
|
||||
|
||||
## Click tracking
|
||||
|
||||
It is possible to track the clicks on every link that is sent in an e-mail. This allows measuring the clickthrough rates of links in e-mails. While this is exceedingly common in e-mail campaigns, it carries privacy implications and should be used in compliance with rules and regulations such as GDPR. It is possible to track link clicks anonymously without associating an e-mail read to a subscriber.
|
||||
|
||||
## Bounce
|
||||
|
||||
A bounce occurs when an e-mail that is sent to a recipient "bounces" back for one of many reasons including the recipient address being invalid, their mailbox being full, or the recipient's e-mail service provider marking the e-mail as spam. EagleCast can automatically process such bounce e-mails that land in a configured POP mailbox, or via APIs of SMTP e-mail providers such as AWS SES and Sengrid. Based on settings, subscribers returning bounced e-mails can either be blocklisted or deleted automatically. [Learn more](bounces.md).
|
||||
@@ -0,0 +1,150 @@
|
||||
# Configuration
|
||||
|
||||
### TOML Configuration file
|
||||
One or more TOML files can be read by passing `--config config.toml` multiple times. Apart from a few low level configuration variables and the database configuration, all other settings can be managed from the `Settings` dashboard on the admin UI.
|
||||
|
||||
To generate a new sample configuration file, run `eaglecast --new-config`
|
||||
|
||||
### Environment variables
|
||||
Variables defined in config.toml can also be provided as environment variables prefixed by `EAGLECAST_` with periods replaced by `__` (double underscore). To start EagleCast purely with environment variables without a configuration file, set the environment variables and pass the config flag as `--config=""`.
|
||||
|
||||
Supported variables:
|
||||
|
||||
| **Environment variable** | Example value |
|
||||
| ------------------------------ | -------------- |
|
||||
| `EAGLECAST_app__address` | "0.0.0.0:9000" |
|
||||
| `EAGLECAST_db__host` | db |
|
||||
| `EAGLECAST_db__port` | 9432 |
|
||||
| `EAGLECAST_db__user` | EagleCast |
|
||||
| `EAGLECAST_db__password` | EagleCast |
|
||||
| `EAGLECAST_db__database` | EagleCast |
|
||||
| `EAGLECAST_db__ssl_mode` | disable |
|
||||
|
||||
|
||||
### Customizing system templates
|
||||
See [system templates](templating.md#system-templates).
|
||||
|
||||
|
||||
### HTTP routes
|
||||
When configuring auth proxies and web application firewalls, use this table.
|
||||
|
||||
#### Private admin endpoints.
|
||||
|
||||
| Methods | Route | Description |
|
||||
| ------- | ------------------ | ----------------------- |
|
||||
| `*` | `/api/*` | Admin APIs |
|
||||
| `GET` | `/admin/*` | Admin UI and HTML pages |
|
||||
| `POST` | `/webhooks/bounce` | Admin bounce webhook |
|
||||
|
||||
|
||||
#### Public endpoints to expose to the internet.
|
||||
|
||||
| Methods | Route | Description |
|
||||
| ----------- | --------------------- | --------------------------------------------- |
|
||||
| `GET, POST` | `/subscription/*` | HTML subscription pages |
|
||||
| `GET, ` | `/link/*` | Tracked link redirection |
|
||||
| `GET` | `/campaign/*` | Pixel tracking image |
|
||||
| `GET` | `/public/*` | Static files for HTML subscription pages |
|
||||
| `POST` | `/webhooks/service/*` | Bounce webhook endpoints for SES, Azure ACS, Sendgrid, and other supported providers |
|
||||
| `GET` | `/uploads/*` | The file upload path configured in media settings |
|
||||
|
||||
|
||||
## Media uploads
|
||||
|
||||
#### Using filesystem
|
||||
|
||||
When configuring `docker` volume mounts for using filesystem media uploads, you can follow either of two approaches. The second option may be necessary if your setup requires you to use `sudo` for docker commands.
|
||||
|
||||
After making any changes you will need to run `sudo docker compose stop ; sudo docker compose up`.
|
||||
|
||||
And under `https://eaglecast.mysite.com/admin/settings` you put `/eaglecast/uploads`.
|
||||
|
||||
#### Using volumes
|
||||
|
||||
Using `docker volumes`, you can specify the name of volume and destination for the files to be uploaded inside the container.
|
||||
|
||||
|
||||
```yml
|
||||
app:
|
||||
volumes:
|
||||
- type: volume
|
||||
source: eaglecast-uploads
|
||||
target: /eaglecast/uploads
|
||||
|
||||
volumes:
|
||||
eaglecast-uploads:
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
This volume is managed by `docker` itself, and you can see find the host path with `docker volume inspect eaglecast_eaglecast-uploads`.
|
||||
|
||||
#### Using bind mounts
|
||||
|
||||
```yml
|
||||
app:
|
||||
volumes:
|
||||
- ./path/on/your/host/:/path/inside/container
|
||||
```
|
||||
Eg:
|
||||
```yml
|
||||
app:
|
||||
volumes:
|
||||
- ./data/uploads:/eaglecast/uploads
|
||||
```
|
||||
The files will be available inside `/data/uploads` directory on the host machine.
|
||||
|
||||
To use the default `uploads` folder:
|
||||
```yml
|
||||
app:
|
||||
volumes:
|
||||
- ./uploads:/eaglecast/uploads
|
||||
```
|
||||
|
||||
## Logs
|
||||
|
||||
### Docker
|
||||
|
||||
https://docs.docker.com/engine/reference/commandline/logs/
|
||||
```
|
||||
sudo docker logs -f
|
||||
sudo docker logs eaglecast_app -t
|
||||
sudo docker logs eaglecast_db -t
|
||||
sudo docker logs --help
|
||||
```
|
||||
Container info: `sudo docker inspect eaglecast_eaglecast`
|
||||
|
||||
Docker logs to `/dev/stdout` and `/dev/stderr`. The logs are collected by the docker daemon and stored in your node's host path (by default). The same can be configured (/etc/docker/daemon.json) in your docker daemon settings to setup other logging drivers, logrotate policy and more, which you can read about [here](https://docs.docker.com/config/containers/logging/configure/).
|
||||
|
||||
### Binary
|
||||
|
||||
EagleCast logs to `stdout`, which is usually not saved to any file. To save EagleCast logs to a file use `./eaglecast > eaglecast.log`.
|
||||
|
||||
Settings -> Logs in admin shows the last 1000 lines of the standard log output but gets erased when EagleCast is restarted.
|
||||
|
||||
For the [service file](https://source.offmarket.win/aleagle/EagleCast/src/branch/master/eaglecast%40.service), you can use `ExecStart=/bin/bash -ce "exec /usr/bin/eaglecast --config /etc/eaglecast/config.toml --static-dir /etc/eaglecast/static >>/etc/eaglecast.log 2>&1"` to create a log file that persists after restarts.
|
||||
|
||||
|
||||
## Time zone
|
||||
|
||||
To change EagleCast's time zone (logs, etc.) edit `docker-compose.yml`:
|
||||
```
|
||||
environment:
|
||||
- TZ=Etc/UTC
|
||||
```
|
||||
with any Timezone listed [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). Then run `sudo docker-compose stop ; sudo docker-compose up` after making changes.
|
||||
|
||||
## SMTP
|
||||
|
||||
### Retries
|
||||
The `Settings -> SMTP -> Retries` denotes the number of times a message that fails at the moment of sending is retried silently using different connections from the SMTP pool. The messages that fail even after retries are the ones that are logged as errors and ignored.
|
||||
|
||||
## SMTP ports
|
||||
Some server hosts block outgoing SMTP ports (25, 465). You may have to contact your host to unblock them before being able to send e-mails. Eg: [Hetzner](https://docs.hetzner.com/cloud/servers/faq/#why-can-i-not-send-any-mails-from-my-server).
|
||||
|
||||
|
||||
## Performance
|
||||
|
||||
### Batch size
|
||||
|
||||
The batch size parameter is useful when working with very large lists with millions of subscribers for maximising throughput. It is the number of subscribers that are fetched from the database sequentially in a single cycle (~5 seconds) when a campaign is running. Increasing the batch size uses more memory, but reduces the round trip to the database.
|
||||
@@ -0,0 +1,45 @@
|
||||
# Developer setup
|
||||
The app has two distinct components, the Go backend and the VueJS frontend. In the dev environment, both are run independently.
|
||||
|
||||
|
||||
### Pre-requisites
|
||||
- `go`
|
||||
- `nodejs` (if you are working on the frontend) and `yarn`
|
||||
- Postgres database. If there is no local installation, the demo docker DB can be used for development (`docker compose up demo-db`)
|
||||
|
||||
|
||||
### First time setup
|
||||
`git clone https://source.offmarket.win/aleagle/EagleCast.git`. The project uses go.mod, so it's best to clone it outside the Go src path.
|
||||
|
||||
1. Copy `config.toml.sample` as `config.toml` and add your config.
|
||||
2. `make dist` to build the EagleCast binary. Once the binary is built, run `./eaglecast --install` to run the DB setup. For subsequent dev runs, use `make run`.
|
||||
|
||||
> [mailhog](https://github.com/mailhog/MailHog) is an excellent standalone mock SMTP server (with a UI) for testing and dev.
|
||||
|
||||
|
||||
### Running the dev environment
|
||||
You can run your dev environment locally or inside containers.
|
||||
|
||||
After setting up the dev environment, you can visit `http://localhost:8080`.
|
||||
|
||||
|
||||
1. Locally
|
||||
|
||||
- Run `make run` to start the eaglecast dev server on `:9000`.
|
||||
- Run `make run-frontend` to start the Vue frontend in dev mode using yarn on `:8080`. All `/api/*` calls are proxied to the app running on `:9000`. Refer to the [frontend README](https://source.offmarket.win/aleagle/EagleCast/src/branch/master/frontend/README.md) for an overview on how the frontend is structured.
|
||||
|
||||
2. Inside containers (Using Makefile)
|
||||
|
||||
- Run `make init-dev-docker` to setup container for db.
|
||||
- Run `make dev-docker` to setup docker container suite.
|
||||
- Run `make rm-dev-docker` to clean up docker container suite.
|
||||
|
||||
3. Inside containers (Using devcontainer)
|
||||
|
||||
- Open repo in vscode, open command palette, and select "Dev Containers: Rebuild and Reopen in Container".
|
||||
|
||||
It will set up db, and start frontend/backend for you.
|
||||
|
||||
|
||||
# Production build
|
||||
Run `make dist` to build the Go binary, build the Javascript frontend, and embed the static assets producing a single self-contained binary, `eaglecast`
|
||||
@@ -0,0 +1,11 @@
|
||||
# Integrating with external systems
|
||||
|
||||
In many environments, a mailing list manager's subscriber database is not run independently but as a part of an existing customer database or a CRM. There are multiple ways of keeping EagleCast in sync with external systems.
|
||||
|
||||
## Using APIs
|
||||
|
||||
The [subscriber APIs](apis/subscribers.md) offers several APIs to manipulate the subscribers database, like addition, updation, and deletion. For bulk synchronisation, a CSV can be generated (and optionally zipped) and posted to the import API.
|
||||
|
||||
## Interacting directly with the DB
|
||||
|
||||
EagleCast uses tables with simple schemas to represent subscribers (`subscribers`), lists (`lists`), and subscriptions (`subscriber_lists`). It is easy to add, update, and delete subscriber information directly with the database tables for advanced usecases. See the [table schemas](https://source.offmarket.win/aleagle/EagleCast/src/branch/master/schema.sql) for more information.
|
||||
@@ -0,0 +1,15 @@
|
||||
# Internationalization (i18n)
|
||||
|
||||
EagleCast comes available in multiple languages thanks to language packs contributed by volunteers. A language pack is a JSON file with a map of keys and corresponding translations. The bundled languages can be [viewed here](https://source.offmarket.win/aleagle/EagleCast/src/branch/master/i18n).
|
||||
|
||||
## Customizing languages
|
||||
|
||||
To customize an existing language or to load a new language, put one or more `.json` language files in a directory, and pass the directory path to EagleCast with the<br />`--i18n-dir=/path/to/dir` flag.
|
||||
|
||||
|
||||
## Contributing a new language
|
||||
|
||||
|
||||
- Copy `i18n/en.json` to a new file named after the language code (e.g. `i18n/xx.json`).
|
||||
- Translate the values in the JSON file.
|
||||
- Send a pull request to add the file to the [i18n directory on the GitHub repo](https://source.offmarket.win/aleagle/EagleCast/src/branch/master/i18n).
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="170" height="32" viewBox="0 0 170 32">
|
||||
<title>EagleCast</title>
|
||||
<g>
|
||||
<circle cx="16" cy="16" r="15" fill="#1b3a5b"/>
|
||||
<path d="M9.2 13.2 C9.2 9.2 12.6 6.6 16.4 6.6 C20.2 6.6 23.3 8.9 24.4 11.9 L30.4 14.2 L24.7 15.9 C24.8 16.5 24.3 17.1 23.4 17.1 L21.7 17.1 C22.5 21.6 20.4 25.1 16.7 26.1 C14.7 26.6 13.1 25.7 12.3 23.9 C10.5 21.0 9.5 17.3 9.2 13.2 Z" fill="#ffffff"/>
|
||||
<path d="M24.4 11.9 L30.4 14.2 L24.7 15.9 C25.0 14.5 24.8 13.1 24.4 11.9 Z" fill="#eaa221"/>
|
||||
<circle cx="19.8" cy="11.9" r="1.5" fill="#1b3a5b"/>
|
||||
</g>
|
||||
<text x="38" y="22.5" font-family="Inter, 'Helvetica Neue', Arial, sans-serif" font-size="18" font-weight="700" fill="#1b3a5b">Eagle<tspan fill="#eaa221">Cast</tspan></text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 769 B |
@@ -0,0 +1,8 @@
|
||||
# Introduction
|
||||
|
||||

|
||||
|
||||
EagleCast is a self-hosted, high performance one-way mailing list and newsletter manager. It comes as a standalone binary and the only dependency is a Postgres database.
|
||||
|
||||
## Developers
|
||||
EagleCast is a free and open source software licensed under AGPLv3. If you are interested in contributing, check out the [GitHub repository](https://source.offmarket.win/aleagle/EagleCast) and refer to the [developer setup](developer-setup.md). The backend is written in Go and the frontend is Vue with Buefy for UI.
|
||||
@@ -0,0 +1,94 @@
|
||||
# Installation
|
||||
|
||||
EagleCast is a simple binary application that requires a Postgres database instance to run. The binary can be downloaded and run manually, or it can be run as a container with Docker compose.
|
||||
|
||||
## Binary
|
||||
1. Download the [latest release](https://source.offmarket.win/aleagle/EagleCast/releases) and extract the EagleCast binary. `amd64` is the main one. It works for Intel and x86 CPUs.
|
||||
1. `./eaglecast --new-config` to generate config.toml. Edit the file.
|
||||
1. `./eaglecast --install` to install the tables in the Postgres DB (⩾ 12).
|
||||
1. Run `./eaglecast` and visit `http://localhost:9000` to create the Super Admin user and login.
|
||||
|
||||
!!! Tip
|
||||
To set the Super Admin username and password during installation, set the environment variables:
|
||||
`EAGLECAST_ADMIN_USER=myuser EAGLECAST_ADMIN_PASSWORD=xxxxx ./eaglecast --install`
|
||||
|
||||
|
||||
## Docker
|
||||
|
||||
Build the `eaglecast` Docker image locally with `make dist` and `docker build`, or push it to a registry of your choice.
|
||||
|
||||
The recommended method is to download the [docker-compose.yml](https://source.offmarket.win/aleagle/EagleCast/src/branch/master/docker-compose.yml) file, customize it for your environment and then to simply run `docker compose up -d`.
|
||||
|
||||
```shell
|
||||
# Download the compose file to the current directory.
|
||||
curl -LO https://source.offmarket.win/aleagle/EagleCast/raw/branch/master/docker-compose.yml
|
||||
|
||||
# Run the services in the background.
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Then, visit `http://localhost:9000` to create the Super Admin user and login.
|
||||
|
||||
!!! Tip
|
||||
To set the Super Admin username and password during setup, set the environment variables (only the first time):
|
||||
`EAGLECAST_ADMIN_USER=myuser EAGLECAST_ADMIN_PASSWORD=xxxxx docker compose up -d`
|
||||
|
||||
|
||||
### Mounting a custom config.toml
|
||||
The docker-compose file includes all necessary EagleCast configuration as environment variables, `EAGLECAST_*`.
|
||||
If you would like to remove those and mount a config.toml instead:
|
||||
|
||||
#### 1. Save the config.toml file on the host
|
||||
|
||||
```toml
|
||||
[app]
|
||||
address = "0.0.0.0:9000"
|
||||
|
||||
# Database.
|
||||
[db]
|
||||
host = "eaglecast_db" # Postgres container name in the compose file.
|
||||
port = 5432
|
||||
user = "eaglecast"
|
||||
password = "eaglecast"
|
||||
database = "eaglecast"
|
||||
ssl_mode = "disable"
|
||||
max_open = 25
|
||||
max_idle = 25
|
||||
max_lifetime = "300s"
|
||||
```
|
||||
|
||||
#### 2. Mount the config file in docker-compose.yml
|
||||
|
||||
```yaml
|
||||
app:
|
||||
...
|
||||
volumes:
|
||||
- /path/on/your/host/config.toml:/eaglecast/config.toml
|
||||
```
|
||||
|
||||
#### 3. Change the `--config ''` flags in the `command:` section to point to the path
|
||||
|
||||
```yaml
|
||||
command: [sh, -c, "./eaglecast --install --idempotent --yes --config /eaglecast/config.toml && ./eaglecast --upgrade --yes --config /eaglecast/config.toml && ./eaglecast --config /eaglecast/config.toml"]
|
||||
```
|
||||
|
||||
-----------
|
||||
|
||||
## Nightly
|
||||
|
||||
!!! Warning
|
||||
Nightly releases are untested and may have bugs. Use at your own risk. Always take a backup of your Postgres database before using a nightly release.
|
||||
|
||||
A nightly build is automatically published with the latest changes merged to the repository. If you want to access the latest changes without waiting for versioned releases, you can obtain the nightly builds and follow the same instructions above.
|
||||
|
||||
- **Docker**: `eaglecast:nightly` (use this as the image name in the docker-compose file)
|
||||
- **Binary**: [Download nightly release](https://source.offmarket.win/aleagle/EagleCast/releases)
|
||||
|
||||
|
||||
## Compiling from source
|
||||
|
||||
To compile the latest unreleased version (`master` branch):
|
||||
|
||||
1. Make sure `go`, `nodejs`, and `yarn` are installed on your system.
|
||||
2. `git clone https://source.offmarket.win/aleagle/EagleCast.git`
|
||||
3. `cd eaglecast && make dist`. This will generate the `eaglecast` binary.
|
||||
@@ -0,0 +1,18 @@
|
||||
# Performance
|
||||
|
||||
EagleCast is built to be highly performant and can handle millions of subscribers with minimal system resources.
|
||||
|
||||
However, as the Postgres database grows—with a large number of subscribers, campaign views, and click records—it can significantly slow down certain aspects of the program, particularly in counting records and aggregating various statistics. For instance, loading admin pages that do these aggregations can take tens of seconds if the database has millions of subscribers.
|
||||
|
||||
- Aggregate counts, statistics, and charts on the landing dashboard.
|
||||
- Subscriber count beside every list on the Lists page.
|
||||
- Total subscriber count on the Subscribers page.
|
||||
|
||||
However, at that scale, viewing the exact number of subscribers or statistics every time the admin panel is accessed becomes mostly unnecessary. On installations with millions of subscribers, where the above pages do not load instantly, it is highly recommended to turn on the `Settings -> Performance -> Cache slow database queries` option.
|
||||
|
||||
## Slow query caching
|
||||
|
||||
When this option is enabled, the subscriber counts on the Lists page, the Subscribers page, and the statistics on the dashboard, etc., are no longer counted in real-time in the database. Instead, they are updated periodically and cached, resulting in a massive performance boost. The periodicity can be configured on the Settings -> Performance page using a standard crontab expression (default: `0 3 * * *`, which means 3 AM daily). Use a tool like [crontab.guru](https://crontab.guru) for easily generating a desired crontab expression.
|
||||
|
||||
## VACUUM-ing
|
||||
Running [`VACUUM ANALYZE`](https://www.postgresql.org/docs/current/sql-vacuum.html) on large Postgres databases at regular intervals (for instance, once a week), is recommended. It reclaims disk space and improves Postgres' query performance. Do note that this is a blocking operation and all database queries can come to a stand-still on a large database while the operation is running (generally only a few seconds).
|
||||
@@ -0,0 +1,35 @@
|
||||
# Messengers
|
||||
|
||||
EagleCast supports multiple custom messaging backends in additional to the default SMTP e-mail backend, enabling not just e-mail campaigns, but arbitrary message campaigns such as SMS, FCM notifications etc.
|
||||
|
||||
A *Messenger* is a web service that accepts a campaign message pushed to it as a JSON request, which the service can in turn broadcast as SMS, FCM etc. Messengers are registered in the *Settings -> Messengers* UI, and can be selected on individual campaigns.
|
||||
|
||||
Messengers support optional BasicAuth authentication. `Plain text` format for campaign content is ideal for messengers such as SMS and FCM.
|
||||
|
||||
When a campaign starts, EagleCast POSTs messages in the following format to the selected messenger's endpoint. The endpoint should return a `200 OK` response in case of a successful request.
|
||||
|
||||
The address required to broadcast the message, for instance, a phone number or an FCM ID, is expected to be stored and relayed as [subscriber attributes](concepts.md/#attributes).
|
||||
|
||||
```json
|
||||
{
|
||||
"subject": "Welcome to EagleCast",
|
||||
"body": "The message body",
|
||||
"content_type": "plain",
|
||||
"recipients": [{
|
||||
"uuid": "e44b4135-1e1d-40c5-8a30-0f9a886c2884",
|
||||
"email": "anon@example.com",
|
||||
"name": "Anon Doe",
|
||||
"attribs": {
|
||||
"phone": "123123123",
|
||||
"fcm_id": "2e7e4b512e7e4b512e7e4b51",
|
||||
"city": "Bengaluru"
|
||||
},
|
||||
"status": "enabled"
|
||||
}],
|
||||
"campaign": {
|
||||
"uuid": "2e7e4b51-f31b-418a-a120-e41800cb689f",
|
||||
"name": "Test campaign",
|
||||
"tags": ["test-campaign"]
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,116 @@
|
||||
|
||||
## OIDC Single Sign On
|
||||
|
||||
EagleCast supports single sign-on with OIDC (OpenID Connect). Any standards compliant OIDC provider can be configured in Settings -> Security -> OIDC
|
||||
|
||||
### User auto-creation
|
||||
If `Settings -> Security -> OIDC -> Auto-create users` is turned on, when users login via OIDC, an account is auto-created if an existing account is not found (based on the OIDC e-mail ID).
|
||||
|
||||
# Tutorials
|
||||
|
||||
Tutorials for configuring EagleCast SSO with popular OIDC providers.
|
||||
|
||||
## Keycloak
|
||||
Keycloak configuration for EagleCast SSO integration.
|
||||
|
||||
### 1. Create a new client in Keycloak
|
||||
In the Keycloak admin, use an existing realm, or create a new realm. Create a new client in `Clients → Create`.
|
||||
|
||||
- **General Settings**
|
||||
- **Client type**: `OpenID Connect`
|
||||
- **Client ID**: `eaglecast` (or any preferred name)
|
||||
- **Name**: Optional descriptive name (e.g., "eaglecast SSO")
|
||||
- **Capability Config**:
|
||||
- **Client authentication**: On
|
||||
- **Authorization**: On
|
||||
- **Authentication Flow**
|
||||
- **Standard Flow**: On
|
||||
- **Direct Access grants**: On
|
||||
- **Login Settings**:
|
||||
- **Root URL**: Copy the **Redirect URL for oAuth provider** value from eaglecast Admin -> Settings -> Security -> OIDC. It will look like `https://eaglecast.yoursite.com/auth/oidc`
|
||||
- **Valid redirect URIs**: Same as the Root URL above
|
||||
- **Valid post logout redirect URIs**: *
|
||||
|
||||
After the client creation steps above, go to the client's `Credentials` tab and copy the `Client Secret`.
|
||||
|
||||
### 2. Configure EagleCast
|
||||
2. In EagleCast Admin -> Settings -> Security -> OIDC.
|
||||
- **Enable OIDC SSO**: Turn on
|
||||
- **Provider URL**: `https://keycloak.yoursite.com/realms/{realm}` (replace `{realm}` with the chosen realm name). This URL is as of v26.3 and may differ across Keycloak versions.
|
||||
- **Provider name**: Set a name to show on the eaglecast login form, eg: `Login with OrgName`
|
||||
- **Client ID**: Client ID set in Keycloak, eg: `eaglecast`
|
||||
- **Client Secret**: Client Secret copied from Keycloak
|
||||
- **Auto-create users from SSO**: (Optional) Enable to automatically create users who don't exist
|
||||
- **Default user role**: (Required if auto-create enabled) Select role for new users
|
||||
|
||||
|
||||
|
||||
## Authentik
|
||||
Authentik configuration for EagleCast SSO integration.
|
||||
|
||||
### 1. Create a new OIDC provider in Authentik
|
||||
In the Authentik admin interface, create a new OIDC provider for EagleCast.
|
||||
|
||||
- **Provider Settings**:
|
||||
- **Name**: `eaglecast` (or any preferred name)
|
||||
- **Signing Key**: `authentik Self-signed Certificate`
|
||||
- **Client Type**: `Confidential`
|
||||
- **Client ID**: `eaglecast` (or any preferred name)
|
||||
- **Redirect URIs**: Copy the **Redirect URL for oAuth provider** value from eaglecast Admin -> Settings -> Security -> OIDC. It will look like `https://eaglecast.yoursite.com/auth/oidc`
|
||||
|
||||
After creating the provider, copy the **Client Secret**.
|
||||
|
||||
### 2. Create an application in Authentik
|
||||
Create a new application and connect it to the newly created provider.
|
||||
|
||||
- **Application Settings**:
|
||||
- **Name**: `eaglecast` (or any preferred name)
|
||||
- **Slug**: `eaglecast` (or any preferred slug. Used in the redirect URL)
|
||||
- **Provider**: Select the OIDC provider created in the previous step
|
||||
|
||||
### 3. Configure EagleCast
|
||||
In EagleCast Admin → Settings → Security → OIDC:
|
||||
|
||||
- **Enable OIDC SSO**: Turn on
|
||||
- **Provider URL**: `https://authentik.yoursite.com/application/o/{slug}/` (replace `{slug}` with the application's slug)
|
||||
- **Provider Name**: Set a name to show on the login form (e.g., `Login with OrgName`)
|
||||
- **Client ID**: Client ID set in Authentik (e.g., `eaglecast`)
|
||||
- **Client Secret**: Client Secret copied from Authentik
|
||||
- **Auto-create users from SSO**: (Optional) Enable to automatically create users who don't exist
|
||||
- **Default user role**: (Required if auto-create enabled) Select role for new users
|
||||
|
||||
## Google Workspace
|
||||
Google Workspace (Google Cloud) configuration for EagleCast SSO integration.
|
||||
|
||||
### 1. Create a new OIDC provider in Google Cloud Console / Google Workspace
|
||||
In the Google Cloud Console interface, create a new Project.
|
||||
|
||||
- **Project Settings**:
|
||||
- **Project name**: `EagleCast` (or any preferred name)
|
||||
- **Branding Settings**:
|
||||
- **App name**: `EagleCast` (or any preferred name, this will be visible to the users.)
|
||||
- **Authorised domains**: `eaglecast.example.com` (or domains that your instance is available on.)
|
||||
|
||||
After creating the project, goto **Clients**.
|
||||
|
||||
### 2. Create an client in project.
|
||||
Create a new client and configure it.
|
||||
|
||||
- **Application Settings**:
|
||||
- **Application type**: `Web application`
|
||||
- **Name**: `eaglecast` (or any preferred name)
|
||||
- **Authorised JavaScript origins**: `https://eaglecast.example.com` (or domains that your instance is available on.)
|
||||
- **Authorised redirect URIs**: `https://eaglecast.example.com/auth/oidc` (or domains that your instance is available on, value is also available in the Settings mentioned above. (Redirect URL for oAuth provider))
|
||||
|
||||
Hit save and note the Client ID and Client Secret
|
||||
|
||||
### 3. Configure EagleCast
|
||||
In EagleCast Admin → Settings → Security → OIDC:
|
||||
|
||||
- **Enable OIDC SSO**: Turn on
|
||||
- **Provider URL**: `https://accounts.google.com` (select Google to Auto-Fill)
|
||||
- **Provider Name**: Set a name to show on the login form (e.g., `Login with OrgName`)
|
||||
- **Client ID**: Client ID copied from Console (e.g., `XXXX.apps.googleusercontent.com`)
|
||||
- **Client Secret**: Client Secret copied from Console
|
||||
- **Auto-create users from SSO**: (Optional) Enable to automatically create users who don't exist
|
||||
- **Default user role**: (Required if auto-create enabled) Select role for new users
|
||||
@@ -0,0 +1,100 @@
|
||||
# Querying and segmenting subscribers
|
||||
|
||||
EagleCast allows the writing of partial Postgres SQL expressions to query, filter, and segment subscribers.
|
||||
|
||||
## Database fields
|
||||
|
||||
These are the fields in the subscriber database that can be queried.
|
||||
|
||||
| Field | Description |
|
||||
| ------------------------ | --------------------------------------------------------------------------------------------------- |
|
||||
| `subscribers.uuid` | The randomly generated unique ID of the subscriber |
|
||||
| `subscribers.email` | E-mail ID of the subscriber |
|
||||
| `subscribers.name` | Name of the subscriber |
|
||||
| `subscribers.status` | Status of the subscriber (`enabled`, `disabled`, `blocklisted`) |
|
||||
| `subscribers.attribs` | Map of arbitrary attributes represented as JSON. Accessed via the `->` and `->>` Postgres operator. |
|
||||
| `subscribers.created_at` | Timestamp when the subscriber was first added |
|
||||
| `subscribers.updated_at` | Timestamp when the subscriber was modified |
|
||||
|
||||
## Sample attributes
|
||||
|
||||
Here's a sample JSON map of attributes assigned to an imaginary subscriber.
|
||||
|
||||
```json
|
||||
{
|
||||
"city": "Bengaluru",
|
||||
"likes_tea": true,
|
||||
"spoken_languages": ["English", "Malayalam"],
|
||||
"projects": 3,
|
||||
"stack": {
|
||||
"frameworks": ["echo", "go"],
|
||||
"languages": ["go", "python"],
|
||||
"preferred_language": "go"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Sample SQL query expressions
|
||||
|
||||
|
||||
#### Find a subscriber by e-mail
|
||||
|
||||
```sql
|
||||
-- Exact match
|
||||
subscribers.email = 'some@domain.com'
|
||||
|
||||
-- Partial match to find e-mails that end in @domain.com.
|
||||
subscribers.email LIKE '%@domain.com'
|
||||
|
||||
```
|
||||
|
||||
#### Find a subscriber by name
|
||||
|
||||
```sql
|
||||
-- Find all subscribers whose name start with John.
|
||||
subscribers.email LIKE 'John%'
|
||||
|
||||
```
|
||||
|
||||
#### Multiple conditions
|
||||
|
||||
```sql
|
||||
-- Find all Johns who have been blocklisted.
|
||||
subscribers.email LIKE 'John%' AND subscribers.status = 'blocklisted'
|
||||
```
|
||||
|
||||
#### Querying subscribers who viewed the campaign email
|
||||
|
||||
```sql
|
||||
-- Find all subscribers who viewed the campaign email.
|
||||
EXISTS(SELECT 1 FROM campaign_views WHERE campaign_views.subscriber_id=subscribers.id AND campaign_views.campaign_id=<put_id_of_campaign>)
|
||||
```
|
||||
|
||||
#### Querying attributes
|
||||
|
||||
```sql
|
||||
-- The ->> operator returns the value as text. Find all subscribers
|
||||
-- who live in Bengaluru and have done more than 3 projects.
|
||||
-- Here 'projects' is cast into an integer so that we can apply the
|
||||
-- numerical operator >
|
||||
subscribers.attribs->>'city' = 'Bengaluru' AND
|
||||
(subscribers.attribs->>'projects')::INT > 3
|
||||
```
|
||||
|
||||
#### Querying nested attributes
|
||||
|
||||
```sql
|
||||
-- Find all blocklisted subscribers who like to drink tea, can code Python
|
||||
-- and prefer coding Go.
|
||||
--
|
||||
-- The -> operator returns the value as a structure. Here, the "languages" field
|
||||
-- The ? operator checks for the existence of a value in a list.
|
||||
subscribers.status = 'blocklisted' AND
|
||||
(subscribers.attribs->>'likes_tea')::BOOLEAN = true AND
|
||||
subscribers.attribs->'stack'->'languages' ? 'python' AND
|
||||
subscribers.attribs->'stack'->>'preferred_language' = 'go'
|
||||
|
||||
```
|
||||
|
||||
To learn how to write SQL expressions to do advancd querying on JSON attributes, refer to the Postgres [JSONB documentation](https://www.postgresql.org/docs/11/functions-json.html).
|
||||
@@ -0,0 +1,62 @@
|
||||
EagleCast supports (>= v4.0.0) creating systems users with granular permissions to various features, including list-specific permissions. Users can login with a username and password, or via an OIDC (OpenID Connect) handshake if an auth provider is connected. Various permissions can be grouped into "user roles", which can be assigned to users. List-specific permissions can be grouped into "list roles".
|
||||
|
||||
## User roles
|
||||
|
||||
A user role is a collection of user related permissions. User roles are attached to user accounts. User roles can be managed in `Admin -> Users -> User roles` The permissions are described below.
|
||||
|
||||
| Group | Permission | Description |
|
||||
| ----------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| lists | lists:get_all | Get details of all lists |
|
||||
| | lists:manage_all | Create, update, and delete all lists |
|
||||
| subscribers | subscribers:get | Get individual subscriber details |
|
||||
| | subscribers:get_all | Get all subscribers and their details |
|
||||
| | subscribers:manage | Add, update, and delete subscribers |
|
||||
| | subscribers:import | Import subscribers from external files |
|
||||
| | subscribers:sql_query | Run raw SQL queries on subscriber data.<br /><span style="color: #de4a45;">**WARNING:**</span><span style="font-size: 0.875em; line-height: 1.3; color:#888;">This permission allows execution of arbitrary SQL expressions and SQL functions. While it is readonly on the table data, it allows querying of all lists and subscribers directly from the database superceding individual list and subscriber permissions. Raw SQL expressions make it possible to obtain Postgres database configuration and potentially interact with other Postgres system features. Give this permission ONLY to trusted users. [Learn more](#subscriberssql_query). |
|
||||
| | tx:send | Send transactional messages to subscribers |
|
||||
| campaigns | campaigns:get | Get and view campaigns belonging to permitted lists |
|
||||
| | campaigns:get_all | Get and view campaigns across all lists |
|
||||
| | campaigns:get_analytics | Access campaign performance metrics |
|
||||
| | campaigns:manage | Create, update, and delete campaigns belonging to permitted lists |
|
||||
| | campaigns:manage_all | Create, update, and delete campaigns across all lists |
|
||||
| | campaigns:send | Start, schedule, pause, resume, and cancel campaigns. This is independent of manage permissions. This is required to send a campaign, even with `campaigns:manage_all` |
|
||||
| bounces | bounces:get | Get email bounce records |
|
||||
| | bounces:manage | Process and handle bounced emails |
|
||||
| | webhooks:post_bounce | Receive bounce notifications via webhook |
|
||||
| media | media:get | Get uploaded media files |
|
||||
| | media:manage | Upload, update, and delete media |
|
||||
| templates | templates:get | Get email templates |
|
||||
| | templates:manage | Create, update, and delete templates |
|
||||
| users | users:get | Get system user accounts |
|
||||
| | users:manage | Create, update, and delete user accounts <span style="color: #de4a45;">**WARNING:**</span><span style="font-size: 0.875em; line-height: 1.3; color:#888;">This permission allows creation of users with any role, including Super Admin. This permission should only be given to Super Admin level accounts</span> |
|
||||
| | roles:get | Get user roles and permissions |
|
||||
| | roles:manage | Create and modify user roles |
|
||||
| settings | settings:get | Get system settings |
|
||||
| | settings:manage | Modify system configuration |
|
||||
| | settings:maintain | Perform system maintenance tasks |
|
||||
|
||||
## List roles
|
||||
|
||||
A list role is a collection of permissions assigned per list. Each list can be assigned a view (read) or manage (update) permission. List roles are attached to user accounts. Only the lists defined in a list role is accessible by the user, be it on the admin UI or via API calls. Do note that the `lists:get_all` and `lists:manage_all` permissions in user roles override all per-list permissions.
|
||||
|
||||
## API users
|
||||
|
||||
A user account can be of two types, a regular user or an API user. API users are meant for intertacting with the EagleCast APIs programmatically. Unlike regular user accounts that have custom passwords or OIDC for authentication, API users get an automatically generated secret token.
|
||||
|
||||
## `subscribers:sql_query`
|
||||
|
||||
This permission allowers users to write and execute arbitrary SQL queries on the database. Although it is executed as a read-only transaction disallowing changing of data in the database tables, it allows querying of all lists, subscribers and other data directly from the database superceding individual list and subscriber permissions.
|
||||
|
||||
Raw SQL expressions also make it possible to obtain Postgres database configuration and potentially interact with other Postgres system features. Give this permission ONLY to trusted users.
|
||||
|
||||
If this permission is being assigned to many users, it is highly recommended that you create a custom Postgres role disallowing any privileged operations. For example:
|
||||
|
||||
```sql
|
||||
CREATE ROLE eaglecast_app WITH
|
||||
LOGIN
|
||||
PASSWORD '...'
|
||||
NOSUPERUSER
|
||||
NOCREATEDB
|
||||
NOCREATEROLE
|
||||
NOREPLICATION;
|
||||
```
|
||||
@@ -0,0 +1,121 @@
|
||||
body[data-md-color-primary="white"] .md-header[data-md-state="shadow"] {
|
||||
background: #fff;
|
||||
box-shadow: none;
|
||||
color: #333;
|
||||
|
||||
box-shadow: 1px 1px 3px #ddd;
|
||||
}
|
||||
|
||||
.md-typeset .md-typeset__table table {
|
||||
border: 1px solid #ddd;
|
||||
box-shadow: 2px 2px 0 #f3f3f3;
|
||||
overflow: inherit;
|
||||
}
|
||||
|
||||
body[data-md-color-primary="white"] .md-search__input {
|
||||
background: #f6f6f6;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
body[data-md-color-primary="white"]
|
||||
.md-sidebar--secondary
|
||||
.md-sidebar__scrollwrap {
|
||||
background: #f6f6f6;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.md-nav__item--section > .md-nav__link[for] {
|
||||
color: #333;
|
||||
}
|
||||
.md-nav__item--section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.md-nav__item--nested .md-nav__list {
|
||||
margin-left: 20px;
|
||||
border-left: 1px solid #ddd;
|
||||
}
|
||||
|
||||
body[data-md-color-primary="white"] a.md-nav__link--active {
|
||||
font-weight: 600;
|
||||
color: inherit;
|
||||
color: #1b3a5b;
|
||||
}
|
||||
body[data-md-color-primary="white"] .md-nav__item a:hover {
|
||||
color: #1b3a5b;
|
||||
}
|
||||
|
||||
body[data-md-color-primary="white"] thead,
|
||||
body[data-md-color-primary="white"] .md-typeset table:not([class]) th {
|
||||
background: #f6f6f6;
|
||||
border: 0;
|
||||
color: inherit;
|
||||
font-weight: 600;
|
||||
}
|
||||
table td span {
|
||||
font-size: 0.85em;
|
||||
color: #bbb;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.md-typeset h1, .md-typeset h2 {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
body[data-md-color-primary="white"] .md-typeset h1 {
|
||||
margin: 4rem 0 0 0;
|
||||
color: inherit;
|
||||
border-top: 1px solid #ddd;
|
||||
padding-top: 2rem;
|
||||
}
|
||||
body[data-md-color-primary="white"] .md-typeset h2 {
|
||||
border-top: 1px solid #eee;
|
||||
padding-top: 2rem;
|
||||
}
|
||||
|
||||
body[data-md-color-primary="white"] .md-content h1:first-child {
|
||||
margin: 0 0 3rem 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
body[data-md-color-primary="white"] .md-typeset code {
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
li img {
|
||||
background: #fff;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e6e6e6;
|
||||
box-shadow: 1px 1px 4px #e6e6e6;
|
||||
padding: 5px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* This hack places the #anchor-links correctly
|
||||
by accommodating for the fixed-header's height */
|
||||
:target:before {
|
||||
content: "";
|
||||
display: block;
|
||||
height: 120px;
|
||||
margin-top: -120px;
|
||||
}
|
||||
|
||||
.md-typeset a {
|
||||
color: #1b3a5b;
|
||||
}
|
||||
.md-typeset a:hover {
|
||||
color: #666 !important;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.md-typeset hr {
|
||||
background: #f6f6f6;
|
||||
margin: 60px 0;
|
||||
display: block;
|
||||
}
|
||||
.md-header--shadow {
|
||||
box-shadow: 0 4px 3px #eee;
|
||||
transition: none;
|
||||
}
|
||||
.md-header__topic:first-child {
|
||||
font-weight: normal;
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
# Templating
|
||||
|
||||
A template is a re-usable HTML design that can be used across campaigns and transactional messages. Most commonly, templates have standard header and footer areas with logos and branding elements, where campaign content is inserted in the middle.
|
||||
|
||||
EagleCast supports [Go template](https://pkg.go.dev/text/template) expressions that lets you create powerful, dynamic HTML templates. It also integrates 100+ useful [Sprig template functions](https://masterminds.github.io/sprig/).
|
||||
|
||||
!!! Warning
|
||||
Sprig template functions are powerful and Turing-complete, allowing programming of complex behaviour in templates. This means that it is also possible to program undesired behaviour, such as overloading memory on the host by concatenating large strings in a loop. Ensure that templating (campaigns, templates) permissions are given only to trusted users.
|
||||
|
||||
## Campaign templates
|
||||
Campaign templates are used in an e-mail campaigns. These template are created and managed on the UI under `Campaigns -> Templates`, and are selected when creating new campaigns.
|
||||
|
||||
## Transactional templates
|
||||
Transactional templates are used for sending arbitrary transactional messages using the transactional API. These template are created and managed on the UI under `Campaigns -> Templates`.
|
||||
|
||||
## Template expressions
|
||||
|
||||
There are several template functions and expressions that can be used in campaign and template bodies. They are written in the form `{{ .Subscriber.Email }}`, that is, an expression between double curly braces `{{` and `}}`. Template expressions are supported in:
|
||||
|
||||
- Campaign body and alt body
|
||||
- Campaign subject
|
||||
- Campaign headers
|
||||
- Transactional message body and alt body
|
||||
- Transactional message subject
|
||||
|
||||
### Subscriber fields
|
||||
|
||||
| Expression | Description |
|
||||
| ----------------------------- | -------------------------------------------------------------------------------------------- |
|
||||
| `{{ .Subscriber.UUID }}` | The randomly generated unique ID of the subscriber |
|
||||
| `{{ .Subscriber.Email }}` | E-mail ID of the subscriber |
|
||||
| `{{ .Subscriber.Name }}` | Name of the subscriber |
|
||||
| `{{ .Subscriber.FirstName }}` | First name of the subscriber (automatically extracted from the name) |
|
||||
| `{{ .Subscriber.LastName }}` | Last name of the subscriber (automatically extracted from the name) |
|
||||
| `{{ .Subscriber.Status }}` | Status of the subscriber (enabled, disabled, blocklisted) |
|
||||
| `{{ .Subscriber.Attribs }}` | Map of arbitrary attributes. Fields can be accessed with `.`, eg: `.Subscriber.Attribs.city` |
|
||||
| `{{ .Subscriber.CreatedAt }}` | Timestamp when the subscriber was first added |
|
||||
| `{{ .Subscriber.UpdatedAt }}` | Timestamp when the subscriber was modified |
|
||||
|
||||
### Campaigns
|
||||
|
||||
| Expression | Description |
|
||||
| --------------------- | -------------------------------------------------------- |
|
||||
| `{{ .Campaign.UUID }}` | The randomly generated unique ID of the campaign |
|
||||
| `{{ .Campaign.Name }}` | Internal name of the campaign |
|
||||
| `{{ .Campaign.Subject }}` | E-mail subject of the campaign |
|
||||
| `{{ .Campaign.FromEmail }}` | The e-mail address from which the campaign is being sent |
|
||||
|
||||
### Functions
|
||||
|
||||
| Function | Description |
|
||||
|--------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `{{ Date "2006-01-01" }}` | Prints the current datetime for the given format expressed as a [Go date layout](https://yourbasic.org/golang/format-parse-string-time-date-example/) |
|
||||
| `{{ TrackLink "https://link.com" }}` | Takes a URL and generates a tracking URL over it. For use in campaign bodies and templates. |
|
||||
| `https://link.com@TrackLink` | Shorthand for `TrackLink`. Eg: `<a href="https://link.com@TrackLink">Link</a>` |
|
||||
| `{{ TrackView }}` | Inserts a single tracking pixel. Should only be used once, ideally in the template footer. |
|
||||
| `{{ UnsubscribeURL }}` | Unsubscription and Manage preferences URL. Ideal for use in the template footer. |
|
||||
| `{{ MessageURL }}` | URL to view the hosted version of an e-mail message. |
|
||||
| `{{ OptinURL }}` | URL to the double opt-in confirmation page. |
|
||||
| `{{ Safe "<!-- comment -->" }}` | Add any HTML code as it is. |
|
||||
|
||||
|
||||
### Sprig functions
|
||||
EagleCast integrates the Sprig library that offers 100+ utility functions for working with strings, numbers, dates etc. that can be used in templating. Refer to the [Sprig documentation](https://masterminds.github.io/sprig/) for the full list of functions.
|
||||
|
||||
|
||||
### Example template
|
||||
|
||||
The expression `{{ template "content" . }}` should appear exactly once in every template denoting the spot where an e-mail's content is inserted. Here's a sample HTML e-mail that has a fixed header and footer that inserts the content in the middle.
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
background: #eee;
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 6px;
|
||||
color: #111;
|
||||
}
|
||||
header {
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding-bottom: 30px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.container {
|
||||
background: #fff;
|
||||
width: 450px;
|
||||
margin: 0 auto;
|
||||
padding: 30px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<section class="container">
|
||||
<header>
|
||||
<!-- This will appear in the header of all e-mails.
|
||||
The subscriber's name will be automatically inserted here. //-->
|
||||
Hi {{ .Subscriber.FirstName }}!
|
||||
</header>
|
||||
|
||||
<!-- This is where the e-mail body will be inserted //-->
|
||||
<div class="content">
|
||||
{{ template "content" . }}
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
Copyright 2019. All rights Reserved.
|
||||
</footer>
|
||||
|
||||
<!-- The tracking pixel will be inserted here //-->
|
||||
{{ TrackView }}
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
!!! info
|
||||
For use with plaintext campaigns, create a template with no HTML content and just the placeholder `{{ template "content" . }}`
|
||||
|
||||
### Example campaign body
|
||||
|
||||
Campaign bodies can be composed using the built-in WYSIWYG editor or as raw HTML documents. Assuming that the subscriber has a set of [attributes defined](querying-and-segmentation.md#sample-attributes), this example shows how to render those values in a campaign.
|
||||
|
||||
```
|
||||
Hey, did you notice how the template showed your first name?
|
||||
Your last name is {{.Subscriber.LastName }}.
|
||||
|
||||
You have done {{ .Subscriber.Attribs.projects }} projects.
|
||||
|
||||
|
||||
{{ if eq .Subscriber.Attribs.city "Bengaluru" }}
|
||||
You live in Bangalore!
|
||||
{{ else }}
|
||||
Where do you live?
|
||||
{{ end }}
|
||||
|
||||
|
||||
Here is a link for you to click that will be tracked.
|
||||
<a href="{{ TrackLink "https://google.com" }}">Google</a>
|
||||
|
||||
```
|
||||
|
||||
The above example uses an `if` condition to show one of two messages depending on the value of a subscriber attribute. Many such dynamic expressions are possible with Go templating expressions.
|
||||
|
||||
## System templates
|
||||
System templates are used for rendering public user-facing pages such as the subscription management page, and in automatically generated system e-mails such as the opt-in confirmation e-mail. These are bundled into EagleCast but can be customized by copying the [static directory](https://source.offmarket.win/aleagle/EagleCast/src/branch/master/static) locally, and passing its path to EagleCast with the `./eaglecast --static-dir=your/custom/path` flag.
|
||||
|
||||
You can fetch the static files with:<br>
|
||||
`mkdir -p /home/ubuntu/eaglecast/static ; wget -O - https://source.offmarket.win/aleagle/EagleCast/archive/master.tar.gz | tar xz -C /home/ubuntu/eaglecast/static --strip=2 "eaglecast-master/static"`
|
||||
|
||||
[Docker example](https://yasoob.me/posts/setting-up-EagleCast-opensource-newsletter-mailing/#custom-static-files), [binary example](https://source.offmarket.win/aleagle/EagleCast/src/branch/master/EagleCast-simple.service).
|
||||
|
||||
|
||||
### Public pages
|
||||
|
||||
| /static/public/ | |
|
||||
|------------------------|--------------------------------------------------------------------|
|
||||
| `index.html` | Base template with the header and footer that all pages use. |
|
||||
| `home.html` | Landing page on the root domain with the login button. |
|
||||
| `message.html` | Generic success / failure message page. |
|
||||
| `optin.html` | Opt-in confirmation page. |
|
||||
| `subscription.html` | Subscription management page with options for data export and wipe. |
|
||||
| `subscription-form.html` | List selection and subscription form page. |
|
||||
|
||||
|
||||
To edit the appearance of the public pages using CSS and Javascript, head to Settings > Appearance > Public:
|
||||
|
||||
|
||||
|
||||
|
||||
### System e-mails
|
||||
|
||||
| /static/email-templates/ | |
|
||||
|----------------------------------|------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `base.html` | Base template with the header and footer that all system generated e-mails use. |
|
||||
| `campaign-status.html` | E-mail notification that is sent to admins on campaign start, completion etc. |
|
||||
| `import-status.html` | E-mail notification that is sent to admins on finish of an import job. |
|
||||
| `subscriber-data.html` | E-mail that is sent to subscribers when they request a full dump of their private data. |
|
||||
| `subscriber-optin.html` | Automatic opt-in confirmation e-mail that is sent to an unconfirmed subscriber when they are added. |
|
||||
| `subscriber-optin-campaign.html` | E-mail content that's inserted into a campaign body when starting an opt-in campaign from the lists page. |
|
||||
| `default.tpl` | Default campaign template that is created in Campaigns -> Templates when EagleCast is first installed. This is not used after that. |
|
||||
|
||||
!!! info
|
||||
To turn system e-mail templates to plaintext, remove `<!doctype html>` from base.html and remove all HTML tags from the templates while retaining the Go templating code.
|
||||
@@ -0,0 +1,82 @@
|
||||
# Upgrade
|
||||
|
||||
!!! Warning
|
||||
Always take a backup of the Postgres database before upgrading eaglecast
|
||||
|
||||
## Binary
|
||||
- Stop the running instance of EagleCast.
|
||||
- Download the [latest release](https://source.offmarket.win/aleagle/EagleCast/releases) and extract the EagleCast binary and overwrite the previous version.
|
||||
- `./eaglecast --upgrade` to upgrade an existing database schema. Upgrades are idempotent and running them multiple times have no side effects.
|
||||
- Run `./eaglecast` again.
|
||||
|
||||
If you installed EagleCast as a service, you will need to stop it before overwriting the binary. Something like `sudo systemctl stop eaglecast` or `sudo service eaglecast stop` should work. Then overwrite the binary with the new version, then run `./eaglecast --upgrade, and `start` it back with the same commands.
|
||||
|
||||
If it's not running as a service, `pkill -9 eaglecast` will stop the EagleCast process.
|
||||
|
||||
## Docker
|
||||
**Important:** The following instructions are for the new [docker-compose.yml](https://source.offmarket.win/aleagle/EagleCast/src/branch/master/docker-compose.yml) file.
|
||||
|
||||
```shell
|
||||
docker compose down app
|
||||
docker compose pull
|
||||
docker compose up app -d
|
||||
```
|
||||
|
||||
If you are using an older docker-compose.yml file, you have to run the `--upgrade` step manually.
|
||||
|
||||
```shell
|
||||
docker-compose down
|
||||
docker-compose pull && docker-compose run --rm app ./eaglecast --upgrade
|
||||
docker-compose up -d app db
|
||||
```
|
||||
|
||||
## Nightly
|
||||
See [here](installation.md#nightly) for instructions on how to access the nightly builds.
|
||||
|
||||
-----------
|
||||
|
||||
## Downgrade
|
||||
|
||||
To restore a previous version, you have to restore the DB for that particular version. DBs that have been upgraded with a particular version shouldn't be used with older versions. There may be DB changes that a new version brings that are incompatible with previous versions.
|
||||
|
||||
**General steps:**
|
||||
|
||||
1. Stop EagleCast.
|
||||
2. Restore your pre-upgrade database.
|
||||
3. If you're using `docker compose`, edit `docker-compose.yml` and change `eaglecast:latest` to `eaglecast:v2.4.0` _(for example)_.
|
||||
4. Restart.
|
||||
|
||||
**Example with docker:**
|
||||
|
||||
1. Stop EagleCast (app):
|
||||
```
|
||||
sudo docker stop eaglecast_app
|
||||
```
|
||||
2. Restore your pre-upgrade db (required) _(be careful, this will wipe your existing DB)_:
|
||||
```
|
||||
psql -h 127.0.0.1 -p 9432 -U eaglecast
|
||||
drop schema public cascade;
|
||||
create schema public;
|
||||
\q
|
||||
psql -h 127.0.0.1 -p 9432 -U eaglecast -W eaglecast < eaglecast-preupgrade-db.sql
|
||||
```
|
||||
3. Edit the `docker-compose.yml`:
|
||||
```
|
||||
x-app-defaults: &app-defaults
|
||||
restart: unless-stopped
|
||||
image: eaglecast:v2.4.0
|
||||
```
|
||||
4. Restart:
|
||||
`sudo docker compose up -d app db nginx certbot`
|
||||
|
||||
|
||||
## Upgrading to v4.x.x
|
||||
v4 is a major upgrade from prior versions with significant changes to certain important features and behaviour. It is the first version to have multi-user support and full fledged user management. Prior versions only had a simple BasicAuth for both admin login (browser prompt) and API calls, with the username and password defined in the TOML configuration file.
|
||||
|
||||
It is safe to upgrade an older installation with `--upgrade`, but there are a few important things to keep in mind. The upgrade automatically imports the `admin_username` and `admin_password` defined in the TOML configuration into the new user management system.
|
||||
|
||||
1. **New login UI**: Once you upgrade an older installation, the admin dashboard will no longer show the native browser prompt for login. Instead, a new login UI rendered by EagleCast is displayed at the URI `/admin/login`.
|
||||
|
||||
1. **API credentials**: If you are using APIs to interact with EagleCast, after logging in, go to Settings -> Users and create a new API user with the necessary permissions. Change existing API integrations to use these credentials instead of the old username and password defined in the legacy TOML configuration file or environment variables.
|
||||
|
||||
1. **Credentials in TOML file or old environment variables**: The admin dashboard shows a warning until the `admin_username` and `admin_password` fields are removed from the configuration file or old environment variables. In v4.x.x, these are irrelevant as user credentials are stored in the database and managed from the admin UI. IMPORTANT: if you are using APIs to interact with EagleCast, follow the previous step before removing the legacy credentials.
|
||||
@@ -0,0 +1,71 @@
|
||||
site_name: EagleCast / Documentation
|
||||
theme:
|
||||
name: material
|
||||
# custom_dir: "mkdocs-material/material"
|
||||
logo: "images/logo.svg"
|
||||
favicon: "images/favicon.png"
|
||||
language: "en"
|
||||
font:
|
||||
text: 'Inter'
|
||||
weights: 400
|
||||
direction: 'ltr'
|
||||
extra:
|
||||
search:
|
||||
language: 'en'
|
||||
feature:
|
||||
tabs: true
|
||||
features:
|
||||
- navigation.indexes
|
||||
- navigation.sections
|
||||
- content.code.copy
|
||||
|
||||
palette:
|
||||
primary: "white"
|
||||
accent: "amber"
|
||||
|
||||
site_dir: _out
|
||||
docs_dir: content
|
||||
|
||||
markdown_extensions:
|
||||
- admonition
|
||||
- pymdownx.highlight
|
||||
- pymdownx.superfences
|
||||
- toc:
|
||||
permalink: true
|
||||
|
||||
extra_css:
|
||||
- "static/style.css"
|
||||
|
||||
copyright: "CC BY-SA 4.0"
|
||||
|
||||
nav:
|
||||
- "Introduction": index.md
|
||||
- "Getting Started":
|
||||
- "Installation": installation.md
|
||||
- "Configuration": configuration.md
|
||||
- "Upgrade": upgrade.md
|
||||
- "Using EagleCast":
|
||||
- "Concepts": concepts.md
|
||||
- "Templating": templating.md
|
||||
- "Querying and segmenting subscribers": querying-and-segmentation.md
|
||||
- "Bounce processing": bounces.md
|
||||
- "Messengers": "messengers.md"
|
||||
- "Archives": "archives.md"
|
||||
- "Internationalization": "i18n.md"
|
||||
- "Integrating with external systems": external-integration.md
|
||||
- "User roles and permissions": roles-and-permissions.md
|
||||
- "OIDC SSO": oidc.md
|
||||
- "API":
|
||||
- "Introduction": apis/apis.md
|
||||
- "Subscribers": apis/subscribers.md
|
||||
- "Lists": apis/lists.md
|
||||
- "Import": apis/import.md
|
||||
- "Campaigns": apis/campaigns.md
|
||||
- "Media": apis/media.md
|
||||
- "Templates": apis/templates.md
|
||||
- "Transactional": apis/transactional.md
|
||||
- "Bounces": apis/bounces.md
|
||||
- "Maintenance":
|
||||
- "Performance": maintenance/performance.md
|
||||
- "Contributions":
|
||||
- "Developer setup": developer-setup.md
|
||||
@@ -0,0 +1,4 @@
|
||||
mkdocs>=1.6.1
|
||||
mkdocs-material>=9.6.14
|
||||
mkdocs-material-extensions>=1.3.1
|
||||
pymdown-extensions>=10.15
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user