diff --git a/v3/api-campaigns.md b/v3/api-campaigns.md new file mode 100644 index 0000000..ef4ab79 --- /dev/null +++ b/v3/api-campaigns.md @@ -0,0 +1,372 @@ +# Campaigns + +## Campaign Object +- data: `object` + - id: `int` (readonly) + - name: `string` + - subject: `string` + - content: `string` + - status_id: `int` (readonly) + - template_id: `int` + - email_service_id: `int` + - from_name: `string` + - from_email: `string` + - is_open_tracking: `bool` + - is_click_tracking: `bool` + - sent_count: `int` (readonly) + - open_count: `int` (readonly) + - click_count: `int` (readonly) + - send_to_all: `bool` + - tags: `array` + - save_as_draft: `bool` + - scheduled_at: `datetime` + - created_at: `datetime` (readonly) + - updated_at: `datetime` (readonly) + +## Index + +Retrieve a paginated list of all campaigns. + +### Usage + +#### Endpoint + +`GET /api/v1/campaigns` + +#### Expected Response Code +200 + +#### Response Fields + +`array` + +#### Sample Request + +``` +GET /api/v1/campaigns HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "name": "name", + "subject": "My Campaign Subject", + "content": "My Email Content", + "status_id": 1, + "template_id": 1, + "email_service_id": 1, + "from_name": "SendPortal", + "from_email": "test@sendportal.io", + "is_open_tracking": true, + "is_click_tracking": true, + "sent_count": 0, + "open_count": 0, + "click_count": 0, + "send_to_all": true, + "tags": [], + "save_as_draft": false, + "scheduled_at": "2020-07-24 08:46:54", + "created_at": "2020-07-24 08:23:38", + "updated_at": "2020-07-24 09:43:42" + } + ], + "links": { + "first": "", + "last": "", + "prev": null, + "next": null + }, + "meta": { + "current_page": 1, + "from": 1, + "last_page": 1, + "path": "", + "per_page": 25, + "to": 1, + "total": 1 + } +} +``` + +## Show + +Get details of a single campaign. + +### Usage + +#### Endpoint + +`GET /api/v1/campaigns/{campaignId}` + +#### Expected Response Code +200 + +#### Response Fields + +`Campaign Object` + +#### Sample Request + +``` +GET /api/v1/campaigns/1 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` + +#### Sample Response + +```json +{ + "data": { + "id": 1, + "name": "name", + "subject": "My Campaign Subject", + "content": "My Email Content", + "status_id": 1, + "template_id": 1, + "email_service_id": 1, + "from_name": "SendPortal", + "from_email": "test@sendportal.io", + "is_open_tracking": true, + "is_click_tracking": true, + "sent_count": 0, + "open_count": 0, + "click_count": 0, + "send_to_all": true, + "tags": [], + "save_as_draft": false, + "scheduled_at": "2020-07-24 08:46:54", + "created_at": "2020-07-24 08:23:38", + "updated_at": "2020-07-24 09:43:42" + } +} +``` + +## Store + +Create a new campaign. + +### Usage + +#### Endpoint + +`POST /api/v1/campaigns` + +#### Expected Response Code +201 + +#### Request Fields + +- name: `string` +- subject: `string` +- content: `string` +- template_id: `int` +- email_service_id: `int` +- from_name: `string` +- from_email: `string` +- is_open_tracking: `bool` (optional) +- is_click_tracking: `bool` (optional) +- send_to_all: `bool` (optional) +- tags: `array` (optional) +- save_as_draft: `bool` (optional) + +#### Response Fields + +`Campaign Object` + +#### Sample Request + +``` +POST /api/campaigns HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "subject": "My New Campaign", + "subject": "My New Campaign Subject", + "content": "My New Email Content" + "template_id": "1" + "email_service_id": "1" + "from_name": "SendPortal" + "from_email": "test@sendportal.io" + "is_open_tracking": "1" + "is_click_tracking": "1" + "send_to_all": "1" + "save_as_draft": "0" + "scheduled_at": "2020-07-24 08:46:54 +} +``` + +#### Sample Response + +```json +{ + "data": { + "id": 1, + "name": "name", + "subject": "My New Campaign Subject", + "content": "My New Email Content", + "status_id": 1, + "template_id": 1, + "email_service_id": 1, + "from_name": "SendPortal", + "from_email": "test@sendportal.io", + "is_open_tracking": true, + "is_click_tracking": true, + "sent_count": 0, + "open_count": 0, + "click_count": 0, + "send_to_all": true, + "tags": [], + "save_as_draft": false, + "scheduled_at": "2020-07-24 08:46:54", + "created_at": "2020-07-24 08:23:38", + "updated_at": "2020-07-24 08:23:38" + } +} +``` + +## Update + +Update a campaign. + +### Usage + +#### Endpoint + +`PUT /api/v1/campaigns/{campaignId}` + +#### Expected Response Code +200 + +#### Request Fields + +- name: `string` +- subject: `string` +- content: `string` +- template_id: `int` +- email_service_id: `int` +- from_name: `string` +- from_email: `string` +- is_open_tracking: `bool` (optional) +- is_click_tracking: `bool` (optional) +- send_to_all: `bool` (optional) +- tags: `array` (optional) +- save_as_draft: `bool` (optional) + +#### Response Fields + +`Campaign Object` + +#### Sample Request + +``` +PUT /api/v1/campaigns/2 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "subject": "My New Campaign Subject", + "content": "My New Email Content" +} +``` + +#### Sample Response + +```json +{ + "data": { + "id": 1, + "name": "name", + "subject": "My New Campaign Subject", + "content": "My New Email Content", + "status_id": 1, + "template_id": 1, + "email_service_id": 1, + "from_name": "SendPortal", + "from_email": "test@sendportal.io", + "is_open_tracking": true, + "is_click_tracking": true, + "sent_count": 0, + "open_count": 0, + "click_count": 0, + "send_to_all": true, + "tags": [], + "save_as_draft": false, + "scheduled_at": "2020-07-24 08:46:54", + "created_at": "2020-07-24 08:23:38", + "updated_at": "2020-07-24 09:43:42" + } +} +``` + +## Send + +Send a campaign. + +### Usage + +#### Endpoint + +`POST /api/v1/campaigns/{campaignId}/send` + +#### Expected Response Code +200 + +#### Request Fields +None + +#### Response Fields + +`Campaign Object` + +#### Sample Request + +``` +POST /api/v1/campaigns/2/send HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json +``` + +#### Sample Response + +```json +{ + "data": { + "id": 1, + "name": "name", + "subject": "My New Campaign Subject", + "content": "My New Email Content", + "status_id": 2, + "template_id": 1, + "email_service_id": 1, + "from_name": "SendPortal", + "from_email": "test@sendportal.io", + "is_open_tracking": true, + "is_click_tracking": true, + "sent_count": 0, + "open_count": 0, + "click_count": 0, + "send_to_all": true, + "tags": [], + "save_as_draft": false, + "scheduled_at": "2020-07-24 08:46:54", + "created_at": "2020-07-24 08:23:38", + "updated_at": "2020-07-24 09:43:42" + } +} +``` diff --git a/v3/api-introduction.md b/v3/api-introduction.md new file mode 100644 index 0000000..a289d4a --- /dev/null +++ b/v3/api-introduction.md @@ -0,0 +1,151 @@ +# Introduction + +## Request Requirements + +When making a request, the following requirements must be met. + +### Headers + +The following headers are required. Failure to supply these headers may lead to the API behaving in unexpected ways. + +#### Accept +An `Accept` header should be provided, with the value `application/json`. + +#### Content-Type +A `Content-Type` header should be supplied with the value `application/json`. + +## Authentication + +Authentication for the API is performed on a per-workspace basis by supplying a token with the API request. + +### Generating Tokens + +To view your existing tokens, or to generate a new token, perform the following steps for each of your Workspaces: + + - Click on your user name in the top right corner + - Select _API Tokens_ from the menu. + - You will see a list of any previously generated API tokens, including when they were generated and an option to delete them. + - To add a new token, optionally provide a description for the token, and then click _Add Token_. + +You will now be able to use the generated token to authenticate with the API for the current workspace. + +### Bearer Token + +The preferred way to authenticate with the API during a request is to use the token as a Bearer token. This is performed by supplying an `Authorization` header with the request, where the value is `Bearer {api_token}`. + +#### Example + +``` +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +``` + +### API Token Parameter + +Alternatively, you can authenticate by providing the token as a parameter when making the request. The token parameter should be keyed as `api_token` where the value is `{api_token}`. + +#### GET Example + +``` +/api/v1/ping?api_token=9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +``` + +#### POST Example + +``` +{ + "api_token": "9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ" +} +``` + +## Throttling + +By default, the API is configured to limit requests to 60 per minute. When this limit is exceeded a `429 Too Many Requests` response will be returned. + +The limit can be configured by editing the `SENDPORTAL_THROTTLE_MIDDLEWARE` key in the`.env` file. The value needs to be in the format `{number_of_requests},{every_X_minutes}`. + +For example, this would limit the API to 1000 requests every 5 minutes: + +```markdown +SENDPORTAL_THROTTLE_MIDDLEWARE=1000,5 +``` + +For more information on rate limiting see the official Laravel documentation [here](https://laravel.com/docs/master/routing#rate-limiting). + +## Testing the API + +You can easily test your API by performing a `GET` request against the `/ping` endpoint: + +`GET /api/v1/ping` + +If your API is working correctly, then this will return a `200` response with the string `ok` in the body content. Note that this endpoint does not require any authentication. + +## Errors + +### Authentication Error + +An authentication error can occur when no token is provided, or the token is invalid. + +#### Response Fields + +- message: `string` + +#### Sample Missing Token Response + +``` +{ + "message": "Unauthenticated." +} +``` + +#### Sample Invalid Token Response + +``` +{ + "message": "Unauthenticated." +} +``` + +## Validation Errors + +When data provided in a request is invalid or missing you will receive an error response explaining what is wrong with the request. + +#### Response Fields + +- message: `string` +- errors: `object` + +#### Sample Response + +``` +{ + "message": "The given data was invalid.", + "errors": { + "email": [ + "The email field is required." + ] + } +} +``` + +## Pagination + +Paginated responses follow this format: + +- data: `array` + - id: `int` + - name: `string` + - created_at: `datetime` + - updated_at: `datetime` +- links: `object` + - first: `string` + - last: `string` + - prev: `string|null` + - next: `string|null` +- meta: `object` + - current_page: `int` + - from: `int` + - last_page: `int` + - path: `string` + - per_page: `int` + - to: `int` + - total: `int` diff --git a/v3/api-subscriber-tags.md b/v3/api-subscriber-tags.md new file mode 100644 index 0000000..85b9ba7 --- /dev/null +++ b/v3/api-subscriber-tags.md @@ -0,0 +1,220 @@ +# Subscriber Tags + +## Index + +Retrieve a list of tags that have been assigned to the given subscriber. + +### Usage + +#### Endpoint + +`GET /api/v1/subscribers/{subscriberId}/tags` + +#### Expected Response Code +200 + +#### Response Fields + +- data: `array` + - id: `int` + - name: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +GET /api/v1/subscribers/1/tags HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "name": "Test Tag", + "created_at": "2020-03-23 12:44:14", + "update_at": "2020-03-23 12:44:14" + } + ] +} +``` + +## Store + +Assigns a list of tags to the given subscriber. Returns a list of tags that have been assigned to the subscriber. + +This endpoint is idempotent, meaning that tags already assigned to the subscriber will not be continuously added on repeated requests to this endpoint. However, if the intention is to supply a full list of tags that should be assigned to the given subscriber, the Update endpoint should be used instead. + +#### Endpoint + +`POST /api/v1/subscribers/{subscriberId}/tags` + +#### Expected Response Code +200 + +#### Request Fields + +- tags: `array` + +#### Response Fields + +- data: `array` + - id: `int` + - name: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +POST /api/v1/subscribers/1/tags HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "tags": [1, 2] +} +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "name": "Test Tag", + "created_at": "2020-03-23 12:44:14", + "update_at": "2020-03-23 12:44:14" + }, + { + "id": 2, + "name": "Test Tag Two", + "created_at": "2020-03-24 08:45:55", + "update_at": "2020-03-24 08:57:21" + } + ] +} +``` + +## Update + +Replaces the list of tags assigned to the given subscriber with the list supplied in the request. + +If you want to assign additional tags to the subscriber without removing existing ones, you should use the Store endpoint. If you want to remove specific tags from the subscriber, you should use the Delete endpoint. + +#### Endpoint + +`PUT /api/v1/subscribers/{subscriberId}/tags` + +#### Expected Response Code +200 + +#### Request Fields + +- tags: `array` + +#### Response Fields + +- data: `array` + - id: `int` + - name: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +PUT /api/v1/subscribers/1/tags HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "tags": [1, 2] +} +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "name": "Test Tag", + "created_at": "2020-03-23 12:44:14", + "update_at": "2020-03-23 12:44:14" + }, + { + "id": 2, + "name": "Test Tag Two", + "created_at": "2020-03-24 08:45:55", + "update_at": "2020-03-24 08:57:21" + } + ] +} +``` + +## Delete + +### Usage + +Removes the given tags from the subscriber. + +#### Endpoint + +`DELETE /api/v1/subscribers/{subscriberId}/tags` + +#### Expected Response Code +200 + +#### Request Fields + +- tags: `array` + +#### Response Fields + +- data: `array` + - id: `int` + - name: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +DELETE /api/v1/subscribers/1/tags HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "tags": [2] +} +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "name": "Test Tag", + "created_at": "2020-03-23 12:44:14", + "update_at": "2020-03-23 12:44:14" + } + ] +} +``` diff --git a/v3/api-subscribers.md b/v3/api-subscribers.md new file mode 100644 index 0000000..fe7ebb3 --- /dev/null +++ b/v3/api-subscribers.md @@ -0,0 +1,307 @@ +# Subscribers + +## Index + +Retrieve a paginated list of all subscribers. + +### Usage + +#### Endpoint + +`GET /api/v1/subscribers` + +#### Expected Response Code +200 + +#### Response Fields + +- data: `array` + - id: `int` + - first_name: `string` + - last_name: `string` + - email: `string` + - unsubscribed_at: `datetime` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +GET /api/v1/subscribers HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "first_name": "Test", + "last_name": "Subscriber", + "email": "testsubscriber@example.com", + "unsubscribed_at": null, + "created_at": "2020-03-23 13:44:09", + "updated_at": "2020-03-23 13:44:09" + }, + { + "id": 2, + "first_name": "Test", + "last_name": "Subscriber Two", + "email": "testsubscriber2@example.com", + "unsubscribed_at": "2020-08-02 08:07:08", + "created_at": "2020-03-23 13:50:39", + "updated_at": "2020-03-23 13:50:39" + } + ], + "links": { + "first": "", + "last": "", + "prev": null, + "next": null + }, + "meta": { + "current_page": 1, + "from": 1, + "last_page": 1, + "path": "", + "per_page": 25, + "to": 2, + "total": 2 + } +} +``` + +## Show + +Retrieve the details of a single subscriber, including its tags. + +### Usage + +#### Endpoint + +`GET /api/v1/subscribers/{subscriberId}` + +#### Expected Response Code +200 + +#### Response Fields + +- data: `object` + - id: `int` + - first_name: `string` + - last_name: `string` + - email: `string` + - tags: `array` + - id: `int` + - string: `string` + - created_at: `datetime` + - updated_at: `datetime` + - unsubscribed_at: `datetime` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +GET /api/v1/subscribers/1 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` + +#### Sample Response + +```json +{ + "data": { + "id": 1, + "first_name": "Test", + "last_name": "Subscriber", + "email": "testsubscriber@example.com", + "tags": [ + { + "id": 1, + "name": "Test Tag", + "created_at": "2020-03-23 12:44:14", + "update_at": "2020-03-23 12:44:14" + } + ], + "unsubscribed_at": null, + "created_at": "2020-03-23 13:44:09", + "updated_at": "2020-03-23 13:44:09" + } +} +``` + +## Store + +Create a new subscriber, optionally including tags that should be assigned to them; or update an existing subscriber based on their email address, optionally including the tags that should be assigned to them. + +This endpoint is overloaded to allow for the update of existing subscribers based on the email address provided in the request. This allows for a workflow where the caller of the API needn't know whether the subscriber is already created; this eliminates the need for developers to create different logical paths in their application and also allows subscribers to be updated without prior knowledge of their internal SendPortal ID. + +The rules for creating new subscribers or updating existing subscribers are as follows: +- If the email address is not currently used for a subscriber in the workspace, then a new subscriber will be created using the email address, name and tags provided +- If the email address is currently used by a subscriber in the workspace, then that subscriber will be updated using the name and tags provided + +### Usage + +#### Endpoint + +`POST /api/v1/subscribers` + +#### Expected Response Code +201 + +#### Request Fields + +- first_name: `string` (optional) +- last_name: `string` (optional) +- email: `string` +- unsubscribed_at: `datetime` (optional) +- tags: `array` (optional) + +#### Response Fields + +- data: `object` + - id: `int` + - first_name: `string` + - last_name: `string` + - email: `string` + - tags: `array` + - id: `int` + - name: `int` + - created_at: `datetime` + - updated_at: `datetime` + - unsubscribed_at: `datetime` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +POST /api/v1/subscribers HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "first_name": "Test", + "last_name": "Subscriber Two", + "email": "testsubscriber2@example.com", + "tags": [1] +} +``` + +#### Sample Response + +```json +{ + "data": { + "id": 2, + "first_name": "Test", + "last_name": "Subscriber two", + "email": "testsubscriber2@example.com", + "tags": [ + { + "id": 1, + "name": "Test Tag", + "created_at": "2020-03-23 12:44:14", + "update_at": "2020-03-23 12:44:14" + } + ], + "unsubscribed_at": null, + "created_at": "2020-03-24 10:43:08", + "updated_at": "2020-03-24 10:43:08" + } +} +``` + +## Update + +#### Endpoint + +`PUT /api/v1/subscribers/{subscriberId}` + +#### Expected Response Code +200 + +#### Description +Update the details of the given subscriber. + +#### Request Fields + +- first_name: `string` (optional) +- last_name: `string` (optional) +- email: `string` +- unsubscribed_at: `datetime` (optional) + +#### Response Fields + +- data: `object` + - id: `int` + - first_name: `string` + - last_name: `string` + - email: `string` + - unsubscribed_at: `datetime` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +PUT /api/v1/subscribers/2 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "first_name": "Test", + "last_name": "Subscriber Two Updated", + "email": "testsubscriber2@example.com", + "tags": [1] +} +``` + +#### Sample Response + +```json +{ + "data": { + "id": 2, + "first_name": "Test", + "last_name": "Subscriber Two Updated", + "email": "testsubscriber2@example.com", + "unsubscribed_at": null, + "created_at": "2020-03-24 10:43:08", + "updated_at": "2020-03-24 10:50:20" + } +} +``` + +## Delete + +Delete the given subscriber. + +### Usage + +#### Endpoint + +`DELETE /api/v1/subscribers/{subscriberId}` + +#### Expected Response Code +204 + +#### Sample Request + +``` +DELETE /api/v1/subscribers/2 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` diff --git a/v3/api-tag-subscribers.md b/v3/api-tag-subscribers.md new file mode 100644 index 0000000..43a158c --- /dev/null +++ b/v3/api-tag-subscribers.md @@ -0,0 +1,238 @@ +# Tag Subscribers + +## Index + +Retrieve a list of subscribers who have been assigned the given tag. + +### Usage + +#### Endpoint + +`GET /api/v1/tags/{tagId}/subscribers` + +#### Expected Response Code +200 + +#### Response Fields + +- data: `array` + - id: `int` + - first_name: `string` + - last_name: `string` + - email: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +GET /api/v1/tags/1/subscribers HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "first_name": "Test", + "last_name": "Subscriber", + "email": "testsubscriber@example.com", + "created_at": "2020-03-23 13:44:09", + "updated_at": "2020-03-23 13:44:09" + } + ] +} +``` + +## Store + +Assign the given tag to a list of subscribers. Returns a list of subscribers that have been assigned the given tag. + +This endpoint is idempotent, meaning that subscribers who have already been assigned the tag will not be continuously tagged on repeated requests to this endpoint. If the intention is to supply a full list of subscribers that should be tagged, the Update endpoint should be used instead. + +#### Endpoint + +`POST /api/v1/tags/{tagId}/subscribers` + +#### Expected Response Code +200 + +#### Request Fields + +- subscribers: `array` + +#### Response Fields + +- data: `array` + - id: `int` + - first_name: `string` + - last_name: `string` + - email: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +POST /api/v1/tags/1/subscribers HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "subscribers": [1, 2] +} +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "first_name": "Test", + "last_name": "Subscriber", + "email": "testsubscriber@example.com", + "created_at": "2020-03-23 13:44:09", + "updated_at": "2020-03-23 13:44:09" + }, + { + "id": 2, + "first_name": "Test", + "last_name": "Subscriber Two", + "email": "testsubscriber2@example.com", + "created_at": "2020-03-23 13:50:39", + "updated_at": "2020-03-23 13:50:39" + } + ] +} +``` + +## Update + +Replace all subscribers assigned a given tag with the list provided in the request. + +If you want to assign a tag to additional subscribers without removing existing ones, you should use the Store endpoint. If you want to remove the tag from specific subscribers, you should use the Delete endpoint. + +#### Endpoint + +`PUT /api/v1/tags/{tagId}/subscribers` + +#### Expected Response Code +200 + +#### Request Fields + +- subscribers: `array` + +#### Response Fields + +- data: `array` + - id: `int` + - first_name: `string` + - last_name: `string` + - email: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +PUT /api/v1/tags/1/subscribers HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "subscribers": [1, 2] +} +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "first_name": "Test", + "last_name": "Subscriber", + "email": "testsubscriber@example.com", + "created_at": "2020-03-23 13:44:09", + "updated_at": "2020-03-23 13:44:09" + }, + { + "id": 2, + "first_name": "Test", + "last_name": "Subscriber Two", + "email": "testsubscriber2@example.com", + "created_at": "2020-03-23 13:50:39", + "updated_at": "2020-03-23 13:50:39" + } + ] +} +``` + +## Delete + +Removes the given tag from the subscribers provided in the request. + +#### Endpoint + +`DELETE /api/v1/tags/{tagId}/subscribers` + +#### Expected Response Code +200 + +#### Request Fields + +- subscribers: `array` + +#### Response Fields + +- data: `array` + - id: `int` + - first_name: `string` + - last_name: `string` + - email: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +DELETE /api/v1/tags/1/subscribers HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "subscribers": [2] +} +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "first_name": "Test", + "last_name": "Subscriber", + "email": "testsubscriber@example.com", + "created_at": "2020-03-23 13:44:09", + "updated_at": "2020-03-23 13:44:09" + } + ] +} +``` diff --git a/v3/api-tags.md b/v3/api-tags.md new file mode 100644 index 0000000..0f213b8 --- /dev/null +++ b/v3/api-tags.md @@ -0,0 +1,247 @@ +# Tags + +## Index + +Retrieve a paginated list of tags. + +### Usage + +#### Endpoint + +`GET /api/v1/tags` + +#### Expected Response Code +200 + +#### Response Fields + +- data: `array` + - id: `int` + - name: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +GET /api/v1/tags HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "name": "Test Tag", + "created_at": "2020-03-23 12:44:14", + "update_at": "2020-03-23 12:44:14" + } + ], + "links": { + "first": "", + "last": "", + "prev": null, + "next": null + }, + "meta": { + "current_page": 1, + "from": 1, + "last_page": 1, + "path": "", + "per_page": 25, + "to": 1, + "total": 1 + } +} +``` + +## Show + +Get details of a single tag. + +### Usage + +#### Endpoint + +`GET /api/v1/tags/{tagId}` + +#### Expected Response Code +200 + +#### Response Fields + +- data: `object` + - id: `int` + - name: `string` + - created_at `datetime` + - updated_at `datetime` + +#### Sample Request + +``` +GET /api/v1/tags/1 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` + +#### Sample Response + +```json +{ + "data": { + "id": 1, + "name": "Test Tag", + "created_at": "2020-03-23 12:44:14", + "update_at": "2020-03-23 12:44:14" + } +} +``` + +## Store + +Create a new tag, optionally including subscribers that should have it assigned to them. + +### Usage + +#### Endpoint + +`POST /api/v1/tags` + +#### Expected Response Code +201 + +#### Request Fields + +- name: `string` +- subscribers: `array` (optional) + +#### Response Fields + +- data: `object` + - id: `int` + - name: `string` + - subscribers: `array` + - id: `int` + - first_name: `string` + - last_name: `string` + - email: `string` + - created_at: `datetime` + - updated_at: `datetime` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +POST /api/tags HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "name": "Test Tag Two", + "subscribers": [1] +} +``` + +#### Sample Response + +```json +{ + "data": { + "id": 2, + "name": "Test Tag Two", + "subscribers": [ + { + "id": 1, + "first_name": "Test", + "last_name": "Subscriber", + "email": "testsubscriber@example.com", + "created_at": "2020-03-23 13:44:09", + "updated_at": "2020-03-23 13:44:09" + } + ], + "created_at": "2020-03-24 08:45:55", + "update_at": "2020-03-24 08:45:55" + } +} +``` + +## Update + +Update the details of the given tag. If you wish to adjust the subscribers associated with the tag, see the Tag Subscribers API. + +### Usage + +#### Endpoint + +`PUT /api/v1/tags/{tagId}` + +#### Expected Response Code +200 + +#### Request Fields + +- name: `string` + +#### Response Fields + +- data: `object` + - id: `int` + - name: `string` + - created_at `datetime` + - updated_at `datetime` + +#### Sample Request + +``` +PUT /api/v1/tags/2 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "name": "Updated Tag" +} +``` + +#### Sample Response + +```json +{ + "data": { + "id": 2, + "name": "Updated Tag", + "created_at": "2020-03-24 08:45:55", + "update_at": "2020-03-24 08:57:21" + } +} +``` + +## Delete + +Delete the given tag. + +#### Endpoint + +`DELETE /api/v1/tags/{tagId}` + +#### Expected Response Code +204 + +#### Sample Request + +``` +DELETE /api/v1/tags/2 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` diff --git a/v3/api-templates.md b/v3/api-templates.md new file mode 100644 index 0000000..7939dfe --- /dev/null +++ b/v3/api-templates.md @@ -0,0 +1,241 @@ +# Templates + +## Index + +Retrieve a paginated list of all templates. + +### Usage + +#### Endpoint + +`GET /api/v1/templates` + +#### Expected Response Code +200 + +#### Response Fields + +- data: `array` + - id: `int` + - name: `string` + - content: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +GET /api/v1/templates HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "name": "Template Name", + "content": "Template Content", + "created_at": "2020-07-17 09:48:07", + "updated_at": "2020-07-17 09:48:07" + } + ], + "links": { + "first": "http://sendportal.test/api/v1/templates?page=1", + "last": "http://sendportal.test/api/v1/templates?page=1", + "prev": null, + "next": null + }, + "meta": { + "current_page": 1, + "from": 1, + "last_page": 1, + "path": "http://sendportal.test/api/v1/templates", + "per_page": 25, + "to": 1, + "total": 1 + } +} +``` + +## Show + +Retrieve the details of a single template. + +### Usage + +#### Endpoint + +`GET /api/v1/templates/{templateId}` + +#### Expected Response Code +200 + +#### Response Fields + +- data: `object` + - id: `int` + - name: `string` + - content: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +GET /api/v1/templates/1 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` + +#### Sample Response + +```json +{ + "data": { + "id": 1, + "name": "Template Name", + "content": "Template Content", + "created_at": "2020-07-17 09:48:07", + "updated_at": "2020-07-17 09:48:07" + } +} +``` + +## Store + +Create a new template. + +### Usage + +#### Endpoint + +`POST /api/v1/templates` + +#### Expected Response Code +201 + +#### Request Fields + +- name: `string` +- content: `string` + +#### Response Fields + +- data: `object` + - id: `int` + - name: `string` + - content: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +POST /api/v1/templates HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "name": "Test Template", + "content": "Test Content" +} +``` + +#### Sample Response + +```json +{ + "data": { + "id": 3, + "name": "Test Template", + "content": "Test Content", + "created_at": "2020-07-17 09:55:10", + "updated_at": "2020-07-17 09:55:10" + } +} +``` + +## Update + +#### Endpoint + +`PUT /api/v1/templates/{templateId}` + +#### Expected Response Code +200 + +#### Description +Update the details of the given template. + +#### Request Fields + +- name: `string` +- content: `string` + +#### Response Fields + +- data: `object` + - id: `int` + - name: `string` + - content: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +PUT /api/v1/templates/3 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "name": "Test Template", + "content": "Content" +} +``` + +#### Sample Response + +```json +{ + "data": { + "id": 3, + "name": "Test Template", + "content": "Content", + "created_at": "2020-07-17 09:55:10", + "updated_at": "2020-07-17 09:57:07" + } +} +``` + +## Delete + +Delete the given template. + +### Usage + +#### Endpoint + +`DELETE /api/v1/templates/{templateId}` + +#### Expected Response Code +204 + +#### Sample Request + +``` +DELETE /api/v1/templates/3 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` diff --git a/v3/campaigns.md b/v3/campaigns.md new file mode 100644 index 0000000..489c6e3 --- /dev/null +++ b/v3/campaigns.md @@ -0,0 +1,83 @@ +# Campaigns + +## Overview + +A campaign is a set of individual email messages that are sent to your [subscribers](/docs/v3/features/subscribers). An example may be a new product launch or a regular newsletter. + +> Before creating a campaign, the following must already be configured: +> - at least one [email service](/docs/v3/email-services/introduction). +> - a list of [subscribers](/docs/v3/features/subscribers) to target with your campaign. +> - an [email template](/docs/v3/features/templates) for your campaign (optional, but recommended). + +## View Campaigns + +To view a listing of your campaigns, click the _Campaigns_ link in the sidebar. This will take you to the campaigns index page. + +## Starting New Campaigns + +Navigate to the campaigns index page and click the _New Campaign_ button. + +### Fields + +#### Campaign Name + +A friendly name for your campaign that is only used within SendPortal. It helps you properly distinguish one campaign from another. An example may be "Newsletter - May 2020". + +#### Subject + +The subject of your campaign email. This is what your mail recipients will see in their inbox, so you should try to keep it clear and concise. The Subject can include placeholders for the Subscriber's `{{first_name}}`, `{{last_name}}` and `{{email}}`. Refer to the Placeholders section of the [Templates](/docs/v3/features/templates) documentation for more information. + +#### From Name + +Also known as the the email sender name. This tells the mail recipients who sent the message. + +#### From Email + +This is what your mail recipients will see as the "from" address. This should be an address that you have ownership of and must match the settings for the selected email service. + +#### Template (optional) + +The template that you wish to use for your campaign email. + +#### Track Opens + +Determines whether or not individual email opens are tracked. If you wish to track opens, webhooks must be configured for your email service. + +#### Track Clicks + +Determines whether or not individual email clicks are tracked. If you wish to track clicks, webhooks must be configured for your email service. + +#### Content + +The body of your campaign email. This is what gets sent to your subscribers, so make it good! + +The content can include placeholders. To understand which placeholders are available to you, see the Placeholders section of the [Templates](/docs/v3/features/templates) documentation. + +Once you have filled out the form, click _Save and Continue_. Your campaign will now be marked as a **Draft**. + +## Sending Campaigns + +Campaigns are dispatched from the preview page. + +### Test Email + +It's good practice to send out a test email before you dispatch your campaign. This allows you to see how your email will appear to your recipients and offers an opportunity to pick up on any mistakes you may have made. To send a test email, simply enter an email address in the `Recipient` field and click _Send Test Email_. It will be dispatched using your configured email service. + +### Sending Options + +#### Recipients + +You can choose to dispatch your campaign to your entire subscriber list or those with specific tags. + +#### Schedule + +You can schedule your campaign to be dispatched immediately or at a later date and time. + +#### Sending Behaviour + +You can specify one of two sending behaviours for your campaign: + +- `Queue Draft` - this will generate each of your messages as a draft and requires you to dispatch each email individually from the _Messages_ section. This is a good idea if you want to double check how each email will display prior to sending, especially if you're using a complex template that may result in each email varying from subscriber to subscriber. We do not recommend this option if you have a large list of subscribers! +- `Send Automatically` - this will generate your messages and dispatch them automatically. + +> If you find that your campaigns do not send, please ensure you have configured scheduled tasks and queue workers as described in the [Additional Configuration](/docs/v3/getting-started/configuration-and-setup#additional-configuration) section. \ No newline at end of file diff --git a/v3/configuration-and-setup.md b/v3/configuration-and-setup.md new file mode 100644 index 0000000..34ebca6 --- /dev/null +++ b/v3/configuration-and-setup.md @@ -0,0 +1,249 @@ +# Configuration & Setup + +## Introduction + +SendPortal comes packaged with a command-line setup utility that will get you up and running in a few minutes. It will attempt to automatically configure SendPortal and create a default user account and workspace for administrative purposes. + +Alternatively, you can configure SendPortal by using the Setup Wizard, or by following the Manual Configuration section below. + +- [Setup Command](#setup-command) (Recommended) +- [Setup Wizard](#setup-wizard) +- [Manual Configuration](#manual-configuration) + +Once you have successfully setup SendPortal, you must also follow the [Additional Configuration](#additional-configuration) instructions below. + +> For security reasons, we recommend setting up SendPortal as soon as possible after installation. + +## Setup Command + +In the SendPortal installation's root directory, run the following command: + +```bash +php artisan sp:install +``` + +The command will prompt you for confirmation on most steps. If this is the first time running setup for an installation, you should answer yes to the prompts in order to ensure that SendPortal is completely set up as expected. + +You can safely run the command multiple times if needed. + +Once the setup command has completed, you will need to move onto the [Additional Configuration](#additional-configuration) section in order to correctly configure how SendPortal dispatches your messages. + +## Setup Wizard + +You can also use a Setup Wizard to guide you through the installation process. If you are hosting SendPortal at `campaigns.example.com`, just point your browser to `campaigns.example.com/setup` to launch the setup process. + + > In order to launch the Setup Wizard the `.env` file must already be present in the root folder of your installation, and the encryption key must be set. See the Manual Configuration section below for more information. + + > Once your first user has been created, the Setup Wizard will no longer be available. + +Once the Setup Wizard has completed, you will need to move onto the [Additional Configuration](#additional-configuration) section in order to correctly configure how SendPortal dispatches your messages. + +## Manual Configuration + +### Creating Configuration File + +SendPortal's configuration is handled through the`.env` file. By default, this file does not exist. To create it manually, you will need to clone the included `.env.example`, rename it to `.env` and make changes as necessary. + +> Any keys that are set in the `.env` file will be used, even if they are blank. If you do not wish to actively set a key, you should remove it from your `.env` file, rather than leave it blank. + +### Key Generation + +If you did not run the setup command, or if for some reason the `APP_KEY` value is empty, you will need to create an encryption key. This is used by SendPortal to apply encryption to things like user sessions. + +To generate a new key, you should run the following command: + +```bash +php artisan key:generate +``` + +> Although key generation can be run again, this will invalidate any sessions or make any stored encrypted data inaccessible. You should not generate a new key unless absolutely necessary. + +### Base URL + +You will need to set the `APP_URL` variable in the `.env` file to the base URL for your installation of SendPortal. + +For example, if you are hosting your installation of SendPortal at `campaigns.example.com`, you will need to set the following: + +``` +APP_URL=https://campaigns.example.com +``` + +This is necessary to allow SendPortal to correctly generate unsubscribe links, or links for user registration emails. + +### Database Connection + +In order for SendPortal to connect to your database, you must set the database configuration values in the `.env` file. + +Firstly, you need to specify what type of database you are using by setting the `DB_CONNECTION` value to either `mysql` for a MySQL database or `pgsql` for a PostgreSQL database. + +Secondly, you need to set the connection details for your database installation. The following values need to be set: + +- `DB_HOST` – This is the host of your database, e.g. `127.0.0.1` for a local installation +- `DB_PORT` - The port SendPortal should use to talk to your database +- `DB_DATABASE` – The database SendPortal should use to store its data +- `DB_USERNAME` – The username SendPortal will use to authenticate itself with your database +- `DB_PASSWORD` – The password SendPortal will use to authenticate itself with your database + +### Database Migrations + +To set up the database schema, migrations must be run. Migrations are instructions an application uses to configure database schema, running in sequence from beginning to end in order to ensure that the database is set up as the application expects it to be. + +> Do not make custom modifications to the database yourself. Any database changes that SendPortal requires should be accomplished through the running of migrations. + +> Before running migrations, ensure that you have correctly configured your database connection, as schema changes will be made. + +The included command-line setup command will run migrations for you (after a prompt), but you can run migrations manually using the following command: + +```bash +php artisan migrate +``` + +### Publishing Vendor Files + +Run the following command to publish the config, views, languages and assets from SendPortal to your project: + +```bash +php artisan vendor:publish --provider=Sendportal\\Base\\SendportalBaseServiceProvider +``` + +### Workspaces & Users + +If you do not use the setup command to create a workspace and user with which to administer SendPortal, you will need to go through the web interface registration process. + +You must follow the User Management Email configuration specified in Additional Configuration in order to allow registration and user invitation in SendPortal. + +## Additional Configuration + +### Cron Jobs + +SendPortal makes use of regular background tasks and it is therefore essential to create a cron job to run every minute: + +```bash +* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1 +``` + +Please refer to the Laravel documentation on [Task Scheduling](https://laravel.com/docs/scheduling) for further information. + +### Message Sending & Queues + +SendPortal sends email messages using a queue system. The queue can be processed synchronously or asynchronously. Asynchronous queues can be handled via your primary database or via redis. + +You can specify which queue driver to use in the `QUEUE_CONNECTION` parameter in the `.env` file. This should be set to one of `sync`, `database` or `redis`, depending on your requirements. This configuration applies to all messages sent in SendPortal and cannot be changed on a per-user, per-workspace or per-provider basis. + +#### Sync + +The synchronous queue runs any queued jobs as they are requested, requiring the user to wait until the job has been completed before any further action can be taken. + +This has the advantage of being simple and requiring no additional configuration. However, as this does not scale well, this approach is only recommended for relatively small message sending requirements. + +To use the synchronous queue, you only need to set the `QUEUE_CONNECTION` to `sync`, and no further configuration is required. + +#### Database + +Processing asynchronous queues via the database is considered a middle option. Running an asynchronous queue avoids the issues that come up using a synchronous queue, as messages can be processed without blocking further action being taken by the user, and will work until all jobs are completed regardless of how long it takes. + +The advantage of using the database for running the asynchronous queue is that it requires no extra services to be run, as it uses the primary database (MySQL or Postgres) that you have already configured for SendPortal. + +The main disadvantage is that under heavy workloads the queue can negatively affect the performance of the database. + +As such, the database queue is recommended for small to medium sized mailing lists. + +To use the database driver, you must first set the `QUEUE_CONNECTION` to `database` in your `.env` file. You also need to run the following commands, which will create a new `jobs` table in your database, which that will be used to manage the queue. + +```bash +php artisan queue:table +php artisan migrate +``` + +#### Redis + +Redis is the recommended solution for running medium to large mailing lists. + +You will of course need an installation of redis on your server. You will then need to set the `QUEUE_CONNECTION` to `redis` and set the following configuration values in your `.env` file: + +- `REDIS_HOST` +- `REDIS_PASSWORD` +- `REDIS_PORT` + +### Running the queue without Laravel Horizon + +If you don't want to use Horizon to manage you redis queue or you're using the database driver you will have to run a [queue worker](https://laravel.com/docs/queues#running-the-queue-worker) for each queue that Sendportal uses. + +- `sendportal-message-dispatch`: dispatches messages to the email service +- `sendportal-webhook-process`: processes incoming webhooks + +``` +php artisan queue:work --queue=sendportal-message-dispatch +php artisan queue:work --queue=sendportal-webhook-process +``` + +### Running Redis Queues With Laravel Horizon + +SendPortal bundles [Laravel Horizon](https://laravel.com/docs/horizon) as an easy way to run and manage redis queues. + +Configuration for the queues necessary to run SendPortal is already included. In order to use Horizon as your queue manager, you should first publish the Horizon assets: + +``` +php artisan horizon:publish +``` + +> Laravel Horizon stopped publishing its assets in version 5.24.4. If you have Horizon 5.24.4 or later installed (`composer show laravel/horizon`), this command only prints a warning and can be skipped. + +To start processing your queue items with Horizon, you simply need to run the following command: + +``` +php artisan horizon +``` + +When using Horizon in production, you should consider using a service to ensure the queue runner restarts if it fails for any reason. The Horizon documentation has a [guide](https://laravel.com/docs/horizon#deploying-horizon) on how to use Supervisor to do this. + +#### Autoscaling + +The configuration for Horizon included with SendPortal allows autoscaling of queue workers. By default, webhooks received and messages sent via the queue each have a minimum of 2 processes running, and a maximum of 10 or 20. If these values do not suit your requirements, they can be adjusted in the `config/horizon.php` file—in particular `supervisor-2` and `supervisor-3`—using the `minProcesses` and `maxProcesses` values. + +### User Management Email + +In order to use user management functionality (for example, inviting new users or password resets) in SendPortal, it is necessary to set up an email service that SendPortal can use to send the messages. + +> If you are not going to be inviting any other users or team members to your SendPortal installation, then this section can be ignored. + +> There is no relationship between SendPortal's internal mail configuration and any email services that are configured for a workspace. + +> You will need to set `SENDPORTAL_REGISTER=true` in the `.env` file in order to use the registration and user invitation functionality. + +> By default, users are granted the ability to reset their passwords. If you wish to disable password resets, you must set `SENDPORTAL_PASSWORD_RESET=false` in the `.env` file. + +You first need to set `MAIL_MAILER` to your chosen service. The options here are `smtp`, `sendmail`, `ses`, `mailgun` and `postmark`. + +#### SMTP & Sendmail + +When using a regular SMTP provider, or sendmail, you should set the following configuration values: + +- `MAIL_HOST` – This is the host for the SMTP server +- `MAIL_PORT` – This is the port that will be used to connect to the SMTP server +- `MAIL_USERNAME` – The username used to authenticate with the SMTP server +- `MAIL_PASSWORD` – The password used to authenticate with the SMTP server +- `MAIL_FROM_ADDRESS` – The address that mail will appear to come from +- `MAIL_FROM_NAME` – The name that mail will appear to come from + +#### SES + +When using SES as your mail service, you should set the following configuration values, adding them to the `.env` file if they are not already present: + +- `AWS_ACCESS_KEY_ID` – Your AWS ID key +- `AWS_SECRET_ACCESS_KEY` – Your AWS secret key +- `AWS_DEFAULT_REGION` – Your AWS region (defaults to `us-east-1` if not included in the configuration file) + +#### Mailgun + +When using Mailgun as your mail service, you should set the following configuration values, adding them to the `.env` file if they are not already present: + +- `MAILGUN_DOMAIN` +- `MAILGUN_SECRET` +- `MAILGUN_ENDPOINT` – (defaults to `api.mailgun.net` if not included in the configuration file) + +#### Postmark + +When using Postmark as your mail service, you should set the following configuration values, adding them to the `.env` file if they are not already present: + +- `POSTMARK_TOKEN` diff --git a/v3/email-service-mailgun.md b/v3/email-service-mailgun.md new file mode 100644 index 0000000..77704d2 --- /dev/null +++ b/v3/email-service-mailgun.md @@ -0,0 +1,43 @@ +# Mailgun + +## Sending Emails + +When you first begin, Mailgun will automatically create a sandbox domain that you can use for sending, but to send using your own domain you'll need to create a custom domain. Select _Sending_ then _Domains_ from the menu on the left hand side, then click _Add New Domain_ (you'll need to add credit card details before this button appears). + +![https://sendportal.io/img/docs/providers/mailgun/mailgun-1.png](https://sendportal.io/img/docs/providers/mailgun/mailgun-1.png) + +Enter the domain name, choose whichever region makes the most sense for you and click _Add Domain_. + +![https://sendportal.io/img/docs/providers/mailgun/mailgun-2.png](https://sendportal.io/img/docs/providers/mailgun/mailgun-2.png) + +You'll now need to visit the site that you use to manage DNS for the domain provided (usually the domain registrar). The steps to do this are beyond the scope of this documentation as they vary from provider to provider, but you will need to create a `TXT` entry for the relevant domain and paste in the strings from the `Hostname` and `Enter This Value` columns. Once you've added these, click _Verify DNS Settings_. + +![https://sendportal.io/img/docs/providers/mailgun/mailgun-3.png](https://sendportal.io/img/docs/providers/mailgun/mailgun-3.png) + +You'll now need to copy the `Private API Key` from _Settings_ > _API Keys_ + +![https://sendportal.io/img/docs/providers/mailgun/mailgun-4.png](https://sendportal.io/img/docs/providers/mailgun/mailgun-4.png) + +Then paste this into the `API Key` field of your SendPortal provider configuration. You will also need to enter your sending `Domain`, and choose the `Zone` (EU or US) that matches the region you chose when adding the domain. + +![https://sendportal.io/img/docs/providers/mailgun/mailgun-5.png](https://sendportal.io/img/docs/providers/mailgun/mailgun-5.png) + +## Tracking + +To enable tracking, select _Sending_ then _Webhooks_ from the menu and click the _Add Webhook_ button. + +![https://sendportal.io/img/docs/providers/mailgun/mailgun-6.png](https://sendportal.io/img/docs/providers/mailgun/mailgun-6.png) + +The URL depends on your domain, but must end with `/api/v1/webhooks/mailgun`. For example, if SendPortal is installed at `https://campaigns.marketing.com`, then each webhook should point to `https://campaigns.marketing.com/api/v1/webhooks/mailgun`. + +![https://sendportal.io/img/docs/providers/mailgun/mailgun-7.png](https://sendportal.io/img/docs/providers/mailgun/mailgun-7.png) + +You'll need to add an entry for each type of event you want to track, but the URL is the same for each of them. + +SendPortal verifies that each webhook was sent by Mailgun, using your Mailgun `HTTP webhook signing key`. You can find this key in the Mailgun dashboard alongside your webhook and API key settings. Paste it into the `Webhook Key` field of your SendPortal provider configuration. + +> If the `Webhook Key` is missing or incorrect, SendPortal will reject Mailgun's webhooks and no tracking data will be recorded. Rejected webhooks are logged with the message `Mailgun webhook failed verification check.` + +![https://sendportal.io/img/docs/providers/mailgun/mailgun-8.png](https://sendportal.io/img/docs/providers/mailgun/mailgun-8.png) + +That's it! You're now setup to send and track e-mails using Mailgun. diff --git a/v3/email-service-mailjet.md b/v3/email-service-mailjet.md new file mode 100644 index 0000000..40eff00 --- /dev/null +++ b/v3/email-service-mailjet.md @@ -0,0 +1,31 @@ +# Mailjet + +## Sending Emails + +To send e-mails using the Mailjet provider, you will need a Mailjet account. If you don't already have an account, visit the [Mailjet website](https://www.mailjet.com/) to create one. + +> The Mailjet Free plan offers you 200 emails a day, which is enough to cover your basic needs if you are a small sender. + +When you first begin, you need to add and configure the _"From:"_ email address, also known as the _Sender Address_. Extensive documentation for this step is available [here](https://app.mailjet.com/support/how-to-add-a-sender-address,96.htm). + +Finally, in order to integrate Sendportal with Mailjet, you need to copy the `API Key` and the `API Secret Key`, which are [generated automatically](https://app.mailjet.com/account/api_keys) when your account is created, into your SendPortal provider configuration. + +![https://sendportal.io/img/docs/providers/mailjet/mailjet-1.png](https://sendportal.io/img/docs/providers/mailjet/mailjet-1.png) + +Alternatively, if you don't want to use the _Master API Key_ (which grants full access to your account), you can set up Sub-account API Keys and limit their scope. + +## Tracking + +By default the tracking options for `opens` and `clicks` are activated for all new accounts. These settings can be changed on your [Account Preferences page](https://app.mailjet.com/account/settings), but keep in mind that Sendportal will always override them with the values that you choose in the application when creating a new campaign. + +To enable tracking however you will have to set up _Webhooks_ from the [Account Information page](https://app.mailjet.com/account/triggers). + +![https://sendportal.io/img/docs/providers/mailjet/mailjet-2.png](https://sendportal.io/img/docs/providers/mailjet/mailjet-2.png) + +The URL depends on your domain, but must end with `/api/v1/webhooks/mailjet`. For example, if SendPortal is installed at `https://campaigns.marketing.com`, then each webhook should point to `https://campaigns.marketing.com/api/v1/webhooks/mailjet`. + +You'll need to add an entry for each type of event you want to track, but the URL is the same for each one of them. + +If you want to limit the number of calls, you can choose to group together events that occurred over the last second. Sendportal is able to handle both single and grouped events. + +That's it! You're now setup to send and track e-mails using Mailjet. diff --git a/v3/email-service-postal.md b/v3/email-service-postal.md new file mode 100644 index 0000000..42099fe --- /dev/null +++ b/v3/email-service-postal.md @@ -0,0 +1,35 @@ +# Postal + +[Postal](https://docs.postalserver.io) is an open-source mail server that you host yourself. + +## Sending Emails + +In your Postal web interface, open the mail server you want to send from and make sure the domain you will be sending from has been added and verified. + +Next, create an API credential for the mail server (from the _Credentials_ section, choose _API_ as the credential type) and copy the generated key. + +In SendPortal, add a new email service, choose `Postal` as the service type and enter the following: + +- `Postal Host` – the hostname of your Postal installation, without `https://`. For example, if your Postal web interface is at `https://postal.example.com`, enter `postal.example.com`. SendPortal always connects to Postal over HTTPS. +- `API Key` – the API credential key you created above. + +## Tracking + +To enable tracking, open the _Webhooks_ section of your Postal mail server and add a new webhook. + +The URL depends on your domain, but must end with `/api/v1/webhooks/postal`. For example, if SendPortal is installed at `https://campaigns.marketing.com`, then the webhook should point to `https://campaigns.marketing.com/api/v1/webhooks/postal`. + +Select only the following events, which are the ones SendPortal handles: + +- `MessageSent` – recorded as delivered +- `MessageLoaded` – recorded as opened +- `MessageLinkClicked` – recorded as clicked +- `MessageBounced` – recorded as a bounce +- `MessageDeliveryFailed` – recorded as a failure +- `MessageHeld` – recorded as a failure + +> SendPortal does not recognise other Postal events. If you send them to SendPortal, they will fail to process: as failed jobs on the `sendportal-webhook-process` queue, or as errors returned to Postal if you use the `sync` queue driver. + +Postal only sends `MessageLoaded` and `MessageLinkClicked` events when open and click tracking is set up for your mail server (Postal calls this a track domain). + +That's it! You're now setup to send and track e-mails using Postal. diff --git a/v3/email-service-postmark.md b/v3/email-service-postmark.md new file mode 100644 index 0000000..ffcec2f --- /dev/null +++ b/v3/email-service-postmark.md @@ -0,0 +1,51 @@ +# Postmark + +## Sending Email + +To send e-mails using the Postmark provider, you will need a Postmark account. If you don't already have an account, visit the [Postmark website](https://postmarkapp.com/) and click the _Start Free Trial_ button in the top right corner of the screen. + +Postmark will automatically create a server for you, called `My First Server`. Click on the server and select the _API Tokens_ tab. + +![https://sendportal.io/img/docs/providers/postmark/postmark-1.png](https://sendportal.io/img/docs/providers/postmark/postmark-1.png) + +Copy the string from `Server API tokens` and create a provider within SendPortal. + +![https://sendportal.io/img/docs/providers/postmark/postmark-2.png](https://sendportal.io/img/docs/providers/postmark/postmark-2.png) + +Next, navigate to _Sender Signatures_ and select the option to _Add Domain_. + +![https://sendportal.io/img/docs/providers/postmark/postmark-3.png](https://sendportal.io/img/docs/providers/postmark/postmark-3.png) + +Enter your domain name and click _Verify domain_. + +![https://sendportal.io/img/docs/providers/postmark/postmark-4.png](https://sendportal.io/img/docs/providers/postmark/postmark-4.png) + +You'll now need to visit the site that you use to manage DNS for the domain provided (usually the domain registrar). The steps to do this are beyond the scope of this documentation as they vary from provider to provider, but you will need to create a `TXT` entry for the relevant domain and paste in the strings from the `Hostname` and `Add this value` columns. + +![https://sendportal.io/img/docs/providers/postmark/postmark-5.png](https://sendportal.io/img/docs/providers/postmark/postmark-5.png) + +Once you've added the entry with your DNS provider, click the _Verify_ button and the DKIM status should transition to `Verified`. It can take some time to make this transition, but if it doesn't work ensure you didn't accidentally copy and paste any whitespace at the beginning or end of either string. + +![https://sendportal.io/img/docs/providers/postmark/postmark-6.png](https://sendportal.io/img/docs/providers/postmark/postmark-6.png) + +To configure e-mail tracking, select the server from the `Servers` page, select the `Transactional` (`Default Transactional Stream`) stream from the list of `Message Streams`. + +![https://sendportal.io/img/docs/providers/postmark/postmark-7.png](https://sendportal.io/img/docs/providers/postmark/postmark-7.png) + +Select the _Webhooks_ tab and click _Add webhook_. + +![https://sendportal.io/img/docs/providers/postmark/postmark-8.png](https://sendportal.io/img/docs/providers/postmark/postmark-8.png) + +The `Webhook URL` depends on your domain, but must end with `/api/v1/webhooks/postmark`. For example, if SendPortal is installed at `https://campaigns.marketing.com`, then each webhook should point to `https://campaigns.marketing.com/api/v1/webhooks/postmark`. + +![https://sendportal.io/img/docs/providers/postmark/postmark-9.png](https://sendportal.io/img/docs/providers/postmark/postmark-9.png) + +You'll also need to enable open tracking and link tracking from the stream settings. + +![https://sendportal.io/img/docs/providers/postmark/postmark-10.png](https://sendportal.io/img/docs/providers/postmark/postmark-10.png) + +That's it! You're now setup to send and track e-mails using Postmark. + +## Message Streams + +SendPortal enables the use of [Postmark message streams](https://postmarkapp.com/message-streams). You can supply this in the `Message Stream` field when adding Postmark as an Email Service. If you do not wish to use Message Streams you can leave this field blank. \ No newline at end of file diff --git a/v3/email-service-sendgrid.md b/v3/email-service-sendgrid.md new file mode 100644 index 0000000..6d424d5 --- /dev/null +++ b/v3/email-service-sendgrid.md @@ -0,0 +1,35 @@ +# Sendgrid + +## Sending Emails + +To send e-mails using the Twilio Sendgrid provider, you will need a Sendgrid account. If you don't already have one, [visit the Sendgrid website](https://sendgrid.com/) and sign up. No credit card is required and you can get up to 100 free emails per month. + +Once registered, open the `Setup Guide`, choose the `Web API` integration and select the PHP language option. + +![https://sendportal.io/img/docs/providers/sendgrid/sendgrid-1.png](https://sendportal.io/img/docs/providers/sendgrid/sendgrid-1.png) + +Generate an API key and copy it to your SendPortal configuration. + +![https://sendportal.io/img/docs/providers/sendgrid/sendgrid-2.png](https://sendportal.io/img/docs/providers/sendgrid/sendgrid-2.png) + +Check the `I've integrated the code above` box and click Next. + +Send a test e-mail from SendPortal and if it works successfully click _Verify integration_. + +## Tracking + +![https://sendportal.io/img/docs/providers/sendgrid/sendgrid-3.png](https://sendportal.io/img/docs/providers/sendgrid/sendgrid-3.png) + +Open and click tracking is enabled by default in SendGrid, but if you'd like to check the settings you'll find them under _Settings_ > _Tracking_. + +![https://sendportal.io/img/docs/providers/sendgrid/sendgrid-4.png](https://sendportal.io/img/docs/providers/sendgrid/sendgrid-4.png) + +You can also enable subscription tracking from this page. If enabled, SendGrid will automatically add unsubscribe links to the bottom of every e-mail you send. + +![https://sendportal.io/img/docs/providers/sendgrid/sendgrid-5.png](https://sendportal.io/img/docs/providers/sendgrid/sendgrid-5.png) + +To enable tracking within SendPortal you'll need to enable event notifications from _Settings_ > _Mail Settings_. + +The `HTTP POST URL` depends on your domain, but must end with `/api/v1/webhooks/sendgrid`. For example, if SendPortal is installed at `https://campaigns.marketing.com`, then each webhook should point to `https://campaigns.marketing.com/api/v1/webhooks/sendgrid`. + +That's it! You're now setup to send and track e-mails using SendGrid. diff --git a/v3/email-service-ses.md b/v3/email-service-ses.md new file mode 100644 index 0000000..0d51464 --- /dev/null +++ b/v3/email-service-ses.md @@ -0,0 +1,131 @@ +# AWS SES + +## Sending Emails + +To send e-mails using the Amazon SES provider, you will need an AWS account. If you don't already have an account, [visit the AWS website](https://aws.amazon.com/) and click the _Create an AWS Account_ button in the top right corner of the screen. + +![https://sendportal.io/img/docs/providers/aws/aws-1.png](https://sendportal.io/img/docs/providers/aws/aws-1.png) + +Be aware that you'll need to provide credit card details, even if you only intend to make use of the free tier. + +Once you've created an account, navigate to the SES (Simple Email Service) control panel by typing "simple email service" into the `Find Services` input on the AWS Management Console and clicking on the relevant option in the dropdown. + +![https://sendportal.io/img/docs/providers/aws/aws-2.png](https://sendportal.io/img/docs/providers/aws/aws-2.png) + +Click _Go to identity management_; + +![https://sendportal.io/img/docs/providers/aws/aws-3.png](https://sendportal.io/img/docs/providers/aws/aws-3.png) + +You'll need to verify the domain from which you want to send all your e-mails, so click _Verify a New Domain_. + +![https://sendportal.io/img/docs/providers/aws/aws-4.png](https://sendportal.io/img/docs/providers/aws/aws-4.png) + +At this stage we're not going to generate the DKIM settings, but this is worth doing once you move to a production environment, as it will reduce the risk of any messages you send being marked as spam. + +![https://sendportal.io/img/docs/providers/aws/aws-5.png](https://sendportal.io/img/docs/providers/aws/aws-5.png) + +Once you click _Verify This Domain_ you'll be provided with a domain verification record; + +![https://sendportal.io/img/docs/providers/aws/aws-6.png](https://sendportal.io/img/docs/providers/aws/aws-6.png) + +You'll now need to visit the site that you use to manage DNS for the domain provided (usually the domain registrar). The steps to do this are beyond the scope of this documentation as they vary from provider to provider, but you will need to create a `TXT` entry for the relevant domain and paste in the strings from the `Name` and `Value` columns. + +Once you've completed this step, click `Close`. At first the `Verification status` will be `pending verification` and `Enabled for sending` will be `No`. + +![https://sendportal.io/img/docs/providers/aws/aws-7.png](https://sendportal.io/img/docs/providers/aws/aws-7.png) + +As long as you have set the DNS entry correctly, the `Verification status` will transition to `verified` and `Enabled for Sending` will show `Yes`. You should also receive a notification via e-mail to say that the domain has been successfully verified. + +![https://sendportal.io/img/docs/providers/aws/aws-8.png](https://sendportal.io/img/docs/providers/aws/aws-8.png) + +It can take some time to make this transition, but if it doesn't work ensure you didn't accidentally copy and paste any whitespace at the beginning or end of either string. + +We also need to create a "configuration set". In order to do that, select the _Configuration Sets_ option from the side menu of the SES panel. + +![https://sendportal.io/img/docs/providers/aws/aws-9.png](https://sendportal.io/img/docs/providers/aws/aws-9.png) + +If you don't require e-mail tracking (delivery, opens etc) you only need to create the configuration set. + +![https://sendportal.io/img/docs/providers/aws/aws-10.png](https://sendportal.io/img/docs/providers/aws/aws-10.png) + +Next we need to generate an access key pair. To do this you'll need to visit the [IAM (Identity and Access Management) module of AWS](https://console.aws.amazon.com/iam/home?#/home). + +It's best to create a new IAM user, so click _Users_ and _Add user_. You can create the key pair using your root user account, but as this allows unrestricted access to the AWS console, it's a security risk if the credentials are ever compromised. + +As the user doesn't need access to the management console, you can just select the `Programmatic access` option. + +![https://sendportal.io/img/docs/providers/aws/aws-12.png](https://sendportal.io/img/docs/providers/aws/aws-12.png) + +If you don't already have any groups configured, you'll need to create one at the next step. If you only want the account to be able to send e-mails then select the `AmazonSESFullAccess` policy. + +![https://sendportal.io/img/docs/providers/aws/aws-13.png](https://sendportal.io/img/docs/providers/aws/aws-13.png) + +If you also want to enable e-mail tracking, you'll need to add the `AmazonSNSFullAccess` policy. + +![https://sendportal.io/img/docs/providers/aws/aws-14.png](https://sendportal.io/img/docs/providers/aws/aws-14.png) + +Selecting tags for the user is optional, so we're not going to enter any at this stage. Once the user has been created you should now be able to copy and paste the access key and secret access key to the SendPortal provider configuration. + +![https://sendportal.io/img/docs/providers/aws/aws-15.png](https://sendportal.io/img/docs/providers/aws/aws-15.png) + +We're now ready to configure the provider with SendPortal. + +![https://sendportal.io/img/docs/providers/aws/aws-11.png](https://sendportal.io/img/docs/providers/aws/aws-11.png) + +Congratulations! You can now send e-mails using Amazon SES. + +If you require any more information about SES, check the official [Amazon SES documentation](https://docs.aws.amazon.com/ses/index.html). + +## Tracking + +If you want to enable tracking for e-mails that you send through SES, then there are a few additional steps we need to complete. + +You'll need to configure Amazon's SNS (Simple Notification Service) in order for it to send the required webhooks to SendPortal, so navigate to the SNS page from the AWS Management Console. + +Begin by creating a topic. Select _Topics_ from the menu and click _Create topic_. Everything other than a name is optional at this time. + +![https://sendportal.io/img/docs/providers/aws/aws-16.png](https://sendportal.io/img/docs/providers/aws/aws-16.png) + +We also need to create a subscription to go along with our topic. For the Topic ARN, select the topic that you just created and select `HTTPS` as the protocol. The `Endpoint` will depend on your domain, but must end with `/api/v1/webhooks/aws`. For example, if SendPortal is installed at `https://campaigns.marketing.com`, then each webhook should point to `https://campaigns.marketing.com/api/v1/webhooks/aws`. + +Do _not_ enable `raw message delivery`, otherwise the webhooks will not be handled correctly. + +![https://sendportal.io/img/docs/providers/aws/aws-17.png](https://sendportal.io/img/docs/providers/aws/aws-17.png) + +The subscription needs to be confirmed before we can use it, but if you've supplied the correct URL for your SendPortal instance, and it's already available to the outside world, we'll do that automatically for you. You should see `Status` transition to `Confirmed`. + +![https://sendportal.io/img/docs/providers/aws/aws-18.png](https://sendportal.io/img/docs/providers/aws/aws-18.png) + +We now need to tell SES to send any e-mail tracking webhooks to this endpoint so navigate back to the SES panel (we're almost there now...). + +Select the domain that you already configured from `Domains` and open the section titled `Notifications`. + +![https://sendportal.io/img/docs/providers/aws/aws-19.png](https://sendportal.io/img/docs/providers/aws/aws-19.png) + +Click _Edit Configuration_ and select the SNS topic that you created from the dropdown for each of the notification types. + +![https://sendportal.io/img/docs/providers/aws/aws-20.png](https://sendportal.io/img/docs/providers/aws/aws-20.png) + +Finally, we need to enable the notification types for the configuration step that we created. Select the configuration set from the list and select SNS from the dropdown next to `Add Destination`. + +![https://sendportal.io/img/docs/providers/aws/aws-21.png](https://sendportal.io/img/docs/providers/aws/aws-21.png) + +Select all the notification types you wish to receive (excluding `Send` and `Rendering Failure`), and select the Topic from the list. + +![https://sendportal.io/img/docs/providers/aws/aws-22.png](https://sendportal.io/img/docs/providers/aws/aws-22.png) + +Phew! That's it folks! You should now be setup to send and track e-mails using Amazon AWS. + +![https://sendportal.io/img/docs/providers/aws/aws-23.png](https://sendportal.io/img/docs/providers/aws/aws-23.png) + +For more information on Amazon SNS, visit the [official documentation](https://docs.aws.amazon.com/sns/latest/dg/sns-getting-started.html). + +For more information on configuring SNS notifications for SES, visit the [developer guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/configure-sns-notifications.html). + +## Throttling + +AWS throttles the sending of e-mails via the SES API. Requests are limited to 1 per second by default, but you can apply to have this rate increased. + +For this reason you need to have a cache configured when using AWS. For a small mailing list the `file` driver is sufficient, but for medium or large mailing lists we recommend implementing `redis`. + +For more information on SES throttling, see the official documentation [here](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/manage-sending-quotas.html?icmpid=docs_ses_console). diff --git a/v3/email-service-smtp.md b/v3/email-service-smtp.md new file mode 100644 index 0000000..c2fb577 --- /dev/null +++ b/v3/email-service-smtp.md @@ -0,0 +1,8 @@ +# SMTP + +## Sending Emails +SendPortal can also send your campaigns via SMTP. If you select this option, then you will __not__ be able to track your campaigns - for example, deliveries, open and click tracking will not be available. + +> For production environments, we recommend using one of the API Email Services instead of SMTP. + +To create an SMTP Email Service in SendPortal, navigate to the **Email Services** tab, click the **Add Email Service** button and enter the relevant details, making sure to select **SMTP** as the type. diff --git a/v3/email-services-introduction.md b/v3/email-services-introduction.md new file mode 100644 index 0000000..87d8a1c --- /dev/null +++ b/v3/email-services-introduction.md @@ -0,0 +1,24 @@ +# Email Services + +## Introduction +SendPortal must be integrated with at least one email service provider in order for your emails to be dispatched. You can have multiple email services configured at one given time. For example, you may wish to send newsletters via SES and transactional emails via Postmark. + +> The credentials for each email service is stored in an encrypted format in the database. If the `APP_KEY` in your `.env` field is changed or lost, you will need to re-enter the credentials for each email service again. + +SendPortal currently integrates with the following email service providers: + +- [AWS SES](/docs/v3/email-services/aws) +- [Postmark](/docs/v3/email-services/postmark) +- [Sendgrid](/docs/v3/email-services/sendgrid) +- [Mailgun](/docs/v3/email-services/mailgun) +- [Mailjet](/docs/v3/email-services/mailjet) +- [Postal](/docs/v3/email-services/postal) +- [SMTP](/docs/v3/email-services/smtp) + +## Testing + +You can test if an Email Service has been correctly configured by clicking on the `Test` button in the page that lists all of your Email Services. + +![https://sendportal.io/img/docs/providers/testing.png](https://sendportal.io/img/docs/providers/testing.png) + +In order to test an Email Service you will have to provide the email address from which the email will be sent - this must be a verified email address or domain. diff --git a/v3/installation.md b/v3/installation.md new file mode 100644 index 0000000..12ea9bb --- /dev/null +++ b/v3/installation.md @@ -0,0 +1,53 @@ +# Installation + +SendPortal can be installed as a stand-alone application (i.e. including everything you need to run SendPortal), or as a package inside an existing Laravel application. + +This page covers the stand-alone installation. If you want to install SendPortal as a package, then head over to the [Package Installation guide](/docs/v3/getting-started/package-installation). + + +## Requirements + +To run SendPortal, your environment must meet a few minimum requirements: + +- PHP ≥ 8.2 +- Git +- [Composer](https://getcomposer.org/) +- MySQL (≥ 5.7) or PostgreSQL (≥ 9.4) + +The stand-alone application is built on Laravel 11. + +> If you are upgrading an existing installation of SendPortal v2, see the [Upgrade Guide](/docs/v3/getting-started/upgrade-guide). + +## Installing SendPortal + +### Clone The Repository + +Clone the repository to your environment, using the following command: + +```bash +git clone --depth 1 --branch v3.0.1 https://github.com/mettle/sendportal.git +``` + +### Install Dependencies + +Once cloned, navigate to the project's root directory and run `composer install` to install SendPortal and its dependencies. + +From here, you can move on to the [Configuration & Setup](/docs/v3/getting-started/configuration-and-setup) step. + +### Webserver + +You will need to use a webserver (for example, Apache or nginx), in order to host your SendPortal installation. + +When setting up your webserver, it should be pointed to the `public` directory in order to correctly serve SendPortal. + +For example, in nginx: + +``` +server { + listen 80; + server_name campaigns.example.com; + root /var/www/campaigns.example.com/public; +} +``` + +For more detailed information, see the [Laravel deployment documentation](https://laravel.com/docs/deployment). diff --git a/v3/messages.md b/v3/messages.md new file mode 100644 index 0000000..b26b420 --- /dev/null +++ b/v3/messages.md @@ -0,0 +1,13 @@ +# Messages + +## Overview + +After a campaign has been dispatched, a message will be generated for each targeted Subscriber. A message represents a single email. You can view all of your messages by clicking the _Messages_ link in the sidebar. You will notice that there are two types of message: + +### Drafts + +A draft is a message that has been queued using the `Queue Draft` campaign sending behaviour. The _Drafts_ tab offers a listing of every drafted message and allows you to preview the final message before sending. A draft message will only be sent to the subscriber when you click on the _Send Now_ button. + +### Sent + +Similarly, the _Sent_ tab offers a listing of every message that has been dispatched. You can use this page to track and filter the status of your messages. diff --git a/v3/package-installation.md b/v3/package-installation.md new file mode 100644 index 0000000..14470ce --- /dev/null +++ b/v3/package-installation.md @@ -0,0 +1,184 @@ +# Package Installation + +SendPortal can be installed as a stand-alone application (i.e. including everything you need to run SendPortal), or as a package inside an existing Laravel application. + +This page covers the Package installation. If you want to install SendPortal as a stand-alone application, then head over to the [Installation guide](/docs/v3/getting-started/installation). + +## Requirements + +- Laravel 10 or 11 +- PHP ≥ 8.2 +- MySQL (≥ 5.7) or PostgreSQL (≥ 9.4) + +## Installation + +To install SendPortal as a package from your command line, run the following command: + +```bash +composer require mettle/sendportal-core +``` + +Alternatively, you can add the following to your `composer.json` file: + +```json +"mettle/sendportal-core": "^3.0" +``` + +and then run `composer update` to install it. + +## Publish Assets +Run the following command to publish SendPortal's assets: + +```bash +php artisan vendor:publish --provider=Sendportal\\Base\\SendportalBaseServiceProvider +``` + +## Resolvers & Routes +SendPortal's functionality is linked to your application through the use of the `Sendportal` facade. The following sections will detail how to use this facade to get SendPortal up and running inside your application. + +> Please read all sections and decide on the relevancy to your use case. Some sections are marked as Required, and will need to be completed for a successful integration with SendPortal. Other sections are marked as Optional and may or may not be required for your own use case. + +## Workspace Resolver (Required) +In order to support multi-tenancy, SendPortal requires a "Workspace ID" for the session. Even if your host application does not use multi-tenancy, you will still need to provide a "dummy" integer. + +The Workspace ID will be used in both web and API sessions and will be stored to the database in the `workspace_id` column of tables like `sendportal_campaigns`, `sendportal_subscribers`, etc. + +> Multi-tenancy is optional. If your application is not multi-tenant, or you do not wish to use SendPortal in a multi-tenant way, you can provide a hardcoded integer value of your choice to the resolver. +> +### Registering the Workspace Resolver +Inside the `boot` method of a service provider class, provide a closure to the `Sendportal\Base\Facades\Sendportal::setCurrentWorkspaceIdResolver()` method that resolves a workspace ID. + +The return value of your closure __must__ be an integer value and __must not__ be null. + +As an example: + +```php + These routes include features like subscriber management and campaign sending, and therefore should not be exposed without some means of authentication. + +```php +// routes/web.php +Route::middleware(['auth'])->prefix('sendportal')->group(function () { + Sendportal::webRoutes(); +}); +``` + +### Public Web Routes (Required) +To provide access to features such as unsubscribing from a mailing list and viewing subscriber emails in the browser, you must register SendPortal's public web routes. + +Inside a routes file, call the `Sendportal\Base\Facades\Sendportal::publicWebRoutes()` method. + +> These routes include publically accessible features such as unsubscribing from a mailing list, and therefore should _not_ be guarded by authentication. + +```php +// routes/web.php +Sendportal::publicWebRoutes(); +``` + +### Public API Routes (Required) +To handle campaign tracking (i.e. webhooks from your email service provider), you must register SendPortal's public API routes. + +> New Laravel 11 applications do not include a `routes/api.php` file. You can create one by running `php artisan install:api`, or register the routes in any other routes file. Webhooks are sent without a CSRF token, so the routes should not use the `web` middleware group. + +Inside a routes file, call the `Sendportal\Base\Facades\Sendportal::publicApiRoutes()` method. + +> These routes include webhooks that are needed to track campaigns inside SendPortal, and must be accessible to your email service. + +```php +// routes/api.php +Sendportal::publicApiRoutes(); +``` + +### Set Up SendPortal API Routes (Optional) +If you wish to provide access to SendPortal's API, then you must register SendPortal's API routes. + +Inside a routes file, call the `Sendportal\Base\Facades\Sendportal::apiRoutes()` method. + +> These routes include features like subscriber and campaign management, and should not be exposed to the internet without some means of authentication. + +The example below uses [Laravel Sanctum](https://laravel.com/docs/sanctum); use whichever guard your application uses to authenticate API requests. + +```php +// routes/api.php +Route::middleware(['auth:sanctum'])->group(function() { + Sendportal::apiRoutes(); +}); +``` + +## Set Up Sidebar HTML Content Resolver (Optional) +Optionally, you can provide content to the sidebar navigation in SendPortal. This allows you to inject new menu items that can link to resources you build yourself. This is helpful for providing access to functionality like user management inside SendPortal. + +### Registering Sidebar HTML Resolver +Inside the `boot` method of a service provider class, provide a closure to the `Sendportal\Base\Facades\Sendportal::setSidebarHtmlContentResolver()` method. + +The return value of your closure __must__ be a string value or null. You should ideally provide HTML, as it will be rendered directly into the view as provided. + +As an example: + +```php + public function boot(): void + { + Sendportal::setSidebarHtmlContentResolver(function () { + return view('layouts.sidebar.manageUsersMenuItem')->render(); + }); + } +``` + +## Set Up Header HTML Content Resolver (Optional) +You can optionally provide content to the header in SendPortal. This allows you to inject resources that you build yourself. This is helpful for providing functionality like user profile management or workspace switchers inside SendPortal. + +### Registering Header HTML Resolver +Inside the `boot` method of a service provider class, provide a closure to the `Sendportal\Base\Facades\Sendportal::setHeaderHtmlContentResolver()` method. + +The return value of your closure __must__ be a string value or null. You should ideally provide HTML, as it will be rendered directly into the view as provided. + +As an example: + +```php + public function boot(): void + { + Sendportal::setHeaderHtmlContentResolver(function () { + return view('layouts.header.userManagementHeader')->render(); + }); + } +``` + +## Further Examples +To see an example of SendPortal used as a fully multi-tenant application, please see our own [host application](https://github.com/mettle/sendportal), which takes advantage of all the integration features available in the SendPortal package. + +In particular, see the `App\Providers\AppServiceProvider` class for registering resolvers, and see the `routes/web.php` and `routes/api.php` files for registering routes. \ No newline at end of file diff --git a/v3/quickstart.md b/v3/quickstart.md new file mode 100644 index 0000000..54c7f0b --- /dev/null +++ b/v3/quickstart.md @@ -0,0 +1,79 @@ +# Quickstart Guide + +## Introduction + +The following guide will walk you through sending out your first campaign. It assumes that you have: + +- successfully installed and configured SendPortal; and +- created your first user; and +- that you are logged in. + +For more detailed instructions for each feature, refer to the relevant article in the Features section. + +## Create An Email Service + +SendPortal integrates with [Mailgun](/docs/v3/email-services/mailgun), [Mailjet](/docs/v3/email-services/mailjet), [Amazon SES](/docs/v3/email-services/aws), [Sendgrid](/docs/v3/email-services/sendgrid), [Postmark](/docs/v3/email-services/postmark) and [SMTP](/docs/v3/email-services/smtp). + +Click on the _Email Services_ link in the sidebar and then click the _Add Email Service_ button. + +The required information will vary between each service. Full instructions on how to set up each service can be found in the [email service documentation](/docs/v3/email-services/introduction). + +## Add Or Import Subscribers + +In order for SendPortal to send emails, you need to set up lists of [subscribers](/docs/v3/features/subscribers). These subscribers can be added in 3 ways: + +- Manually +- Through the API +- CSV Import + +To add a single subscriber manually, click the _Subscribers_ link in the sidebar, and then click _New Subscriber_. You will need to provide an email address for the subscriber and ensure that `Subscribed` is checked. Once you are happy with your subscriber, click _Save_. + +Refer to the [subscriber documentation](/docs/v3/features/subscribers) for more information. + +## Create A Template + +A [template](/docs/v3/features/templates) allows you to create consistent branding for all your campaigns, so that you only need to create new content for each campaign. + +> Campaigns can be created without a template as well, if desired. + +To create a template, click on the _Templates_ link in the sidebar and then click the _Add Template_ button. Templates should contain valid HTML and must include at least a `{{content}}` tag. + +Refer to the [templates documentation](/docs/v3/features/templates) for more information. + +## Create A Campaign + +To create a campaign, click on the _Campaigns_ link in the sidebar and then click the _New Campaign_ button. + +If you have created a template in the previous step, you can select it here. The content will be placed into the `{{content}}` tag in the template. + +Refer to the [campaign documentation](/docs/v3/features/campaigns) for more information. + +## Send The Campaign + +After your campaign has been created, you will have a chance to review it before sending. + +### Test Email + +You can test the email by entering an address in the Test Email section and clicking _Send Test Email_. + +### Recipients + +Specify who the campaign should be sent to; either **All Subscribers,** or those with one or more **tags** assigned to them. + +### Schedule + +Choose to dispatch the campaign immediately or at a specific and date and time. + +### Sending Behaviour + +Finally, you can choose to save each individual email as a draft message, or send it out immediately. + +Choosing to save the message as a draft means you will will need to review and send each individual message before it is sent out. + +Choosing to send the messages immediately will dispatch each message as soon as SendPortal is ready to do so. + +## Next Steps + +This covers the basics of how to set up your email services, templates, subscribers and campaigns. + +For more detailed information on each of these items, please refer to the relevant Features section of the documentation. diff --git a/v3/subscribers.md b/v3/subscribers.md new file mode 100644 index 0000000..2fd5319 --- /dev/null +++ b/v3/subscribers.md @@ -0,0 +1,113 @@ +# Subscribers + +## Overview + +Subscribers represent individuals that are available to you when sending out campaigns. Subscribers can optionally be assigned one or more [tags](/docs/v3/features/tags). + +> The email address for each Subscriber must be unique in each [workspace](/docs/v3/features/workspaces). + +## View Subscribers + +To view a list of your subscribers, click the _Subscribers_ link in the sidebar. This will take you to the subscribers index page. + +### Subscriber Table + +Subscribers are listed in a paginated table, where each row represents a different subscriber. + +### Filtering Subscribers + +The following filtering options are available: + +- Subscriber Email and/or Name. Partial matches will also be returned. +- Subscriber Subscription Status + +## Viewing an Individual Subscriber + +To view an individual subscriber, head to the subscribers index page and click on the subscriber's email in the table that's presented to you. + +### Subscriber Details + +Each subscriber's email, first name, last name and subscription status is displayed when viewing an individual subscriber. + +In addition, if the subscriber has been assigned any [tags](/docs/v3/features/tags) then these are also displayed. + +### Messages + +If any messages have been sent to the subscriber, they will be listed in the Messages section. + +## Adding and Updating Subscribers + +Subscribers can be added and updated in the following ways: + +* manually through the application interface +* via a CSV upload +* programmatically using the API. + +### Application Interface + +#### Adding A Subscriber + +Navigate to the subscribers index page and click _New Subscriber_. + +#### Updating A Subscriber + +Click the _Edit_ button on the subscriber's row in the list. + +The following information can be saved for each Subscriber: + +- email +- first name (optional) +- last name (optional) +- subscription status +- tags (optional) + +### CSV Imports + +SendPortal allows you to easily import multiple subscribers via a CSV upload. This can be used to create new subscribers and update existing ones. + +To import subscribers via CSV, click the _Subscribers_ link in the sidebar. From there, click the overflow menu button and then _Import Subscribers_. + +#### CSV File Format + +The following fields are available in the CSV. + +- `id` (optional) - The ID field is only used when updating existing subscribers, and must be set if you wish to update the subscriber's email address. The `id` field should be present but left empty when importing new subscribers. +- `email` (required) - A unique email address for the subscriber. Note that if the email address is already present in SendPortal, the row will instead update the `first_name` and `last_name` of the existing subscriber. +- `first_name` (optional) - The subscriber's first name. +- `last_name` (optional) - The subscriber's last name. + +Additionally, the **first row** of the CSV **must** be a header row, where each column is the name as listed above, and must be provided in the order above. + +Each row after the header row must represent a single subscriber. Optional fields can be left empty. + +> The import page shows an example of a CSV file, with the header and a row. + +#### Uploading The File + +To upload the file, click the _Browse_ button on the file selector, and choose the file you wish to upload. When you have selected your file, you can click the _Upload_ button to begin the upload process. + +#### Tags + +Tags can be assigned to subscribers by using the `Tags` selector. If you have any tags available, you can pick which ones to assign here. Note that the chosen tags will apply to all subscribers uploaded in the chosen CSV. It is currently not possible to specify separate tags for each individual subscriber. + +### API + +Refer to the [subscribers API documentation](/docs/v3/api/subscribers) for information on how to manage subscribers through the API. + +## Exporting Subscribers + +To export subscribers, click the _Subscribers_ link in the sidebar. From there, click the overflow menu button and then _Export Subscribers_. + +## Unsubscribing + +### Manually Unsubscribing + +To manually unsubscribe a subscriber, you should go to the subscriber edit page, uncheck `Subscribed` and then click _Save_. + +### Unsubscribe Links + +SendPortal allows automatic injection of unsubscribe links in your templates and campaigns. See the [templates](/docs/v3/features/templates) section for more information on how this works. + +When a user clicks on the unsubscribe link, they will be sent to a web page asking them to confirm that they want to be unsubscribed. + +If they decide to unsubscribe, they will be shown a confirmation message that they have been unsubscribed, along with the option to resubscribe. diff --git a/v3/tags.md b/v3/tags.md new file mode 100644 index 0000000..ee388a6 --- /dev/null +++ b/v3/tags.md @@ -0,0 +1,45 @@ +# Tags + +## Overview + +Tags offer a convenient means of grouping subscribers together. For example, you may have one tag for your monthly newsletter and another for customers that want to be notified of a new product launch. + +Tags are completely optional; a subscriber can be assigned one tag, multiple tags, or none at all. + +## View Tags + +To view a listing of your tags, click the _Tags_ button on the subscribers index page. This will take you to the tags index. + +## Adding and Updating Tags + +Tags can be added in the following ways: + +- through the application interface +- via the API + +### Application Interface + +To add a new tag through the application interface, navigate to the tags index page and click _New Tag_. + +To edit a tag, click the _Edit_ button on the tag listing. + +Tags are very simple and only require a `Name`, which must be unique across all tags in the current workspace. + +### API + +Refer to the [Tags API Documentation](/docs/v3/api/tags) for information on how to manage tags through the API. + +## Deleting Tags + +Tags can be deleted in the following ways: + +- through the application interface +- via the API + +### Application Interface + +To delete a tag through the application interface, navigate to the tags index page. Once there, find your target tag in the listing and click the _Delete_ button. + +### API + +Refer to the [Tags API Documentation](/docs/v3/api/tags) for information on how to delete tags through the API. \ No newline at end of file diff --git a/v3/templates.md b/v3/templates.md new file mode 100644 index 0000000..70d1a8e --- /dev/null +++ b/v3/templates.md @@ -0,0 +1,56 @@ +# Templates + +## Overview + +Templates make it easy for you to reuse content and designs across your campaigns. You may, for example, want to create a template for your monthly newsletter. This would make it easier for you to keep the design of your emails consistent and reduce the effort that's needed to prepare your newsletter each month. Our dynamic templating system also allows you to insert subscriber details into your templates, such as their name. + +## View Templates + +To view a listing of your templates, click the _Templates_ link in the sidebar. This will take you to the templates index page. + +## Creating and Updating Templates + +To add a template, navigate to the templates index page and then click the _New Template_ button. + +A template requires the following: + +- A name +- Contents of the template + +The contents of a template must be valid HTML. + +Additionally, the template **must** include a `{{content}}` placeholder to inject the contents of a campaign. To set the placeholder, you should include the following at the desired place in your template: + +``` +{{content}} +``` + +When sending a [campaign](/docs/v3/features/campaigns), the campaign's content will be substituted for this placeholder. + +### Placeholders + +As well as the required `{{content}}` placeholder, the following placeholders are available in templates: + +- `{{email}}` – The subscriber's email +- `{{first_name}}` – The subscriber's first name (if set) +- `{{last_name}}` – The subscriber's last name (if set) +- `{{unsubscribe_url}}` – A URL generated to allow the subscriber to unsubscribe +- `{{webview_url}}` – A URL generated to allow the subscriber to view the message in a web view + +### URL Placeholders + +The URL placeholders (`{{unsubscribe_url}}` and `{{webview_url}}`) generate the URL only; they do not create a clickable link such as an HTML anchor tag. + +This means that simply placing one of these placeholders in the template will result in the URL being displayed as plain text. You are required to create an appropriate anchor tag and include the URL as the `href` property if you wish to create clickable links. For example: + +```html +Click here to unsubscribe +``` + +### Inline CSS Styles + +Although external stylesheets and embedded styles (i.e. including the styles in the `` of an HTML page) work well for web pages, many email clients will ignore or strip out these elements. + +SendPortal automatically inlines all of your CSS styles when the emails are dispatched. You can therefore continue to use the convenience of CSS and/or embedded styles in your templates, but still ensure that your emails display correctly for your subscribers. + +If your template contains CSS and inline styles, the inline styles will be retained, and the CSS and/or embedded styles will be added to the existing inline styles. diff --git a/v3/upgrade-guide.md b/v3/upgrade-guide.md new file mode 100644 index 0000000..12d375f --- /dev/null +++ b/v3/upgrade-guide.md @@ -0,0 +1,130 @@ +# SendPortal v2 to v3 Upgrade Guide + +> Please see the "Notable Changes" section at the bottom of this document for a list of changes that could impact your use of SendPortal after the upgrade. + +> If you do not wish to upgrade to v3, please see the "Remaining On v2" section at the bottom of this document. + +## Requirements + +> We recommend creating a database backup for your SendPortal installation before proceeding with the v3 upgrade. + +### PHP +SendPortal v3 bumps the minimum supported version of PHP from 7.3 to __8.2__. + +> We recommend using the latest version of PHP possible, as older versions no longer receive bug fixes or security updates. + +### Laravel +The stand-alone SendPortal application now runs on Laravel 11. + +If you use SendPortal as a package, your application must be running Laravel 10 or 11 before you upgrade. See the [Laravel upgrade guide](https://laravel.com/docs/11.x/upgrade) for details on upgrading your application. + +## Updating To The Latest Version +To get the latest version of SendPortal, you must checkout the new v3 tag. + +First, ensure you are in your SendPortal installation directory: + +``` +cd /path/to/sendportal +``` + +Then use git to fetch and checkout the `v3.0.1` tag: + +``` +git fetch --tags +git checkout v3.0.1 +``` + +> If you have made your own changes to SendPortal's files, git may not be able to checkout the new tag until you commit or stash those changes, and you may need to resolve conflicts with the changes made for Laravel 11. + +Finally, update SendPortal's dependencies: + +``` +composer update +``` + +## Performing The Upgrade +Once you have the new version of SendPortal on your system, you will need to perform an upgrade to ensure your installation works correctly. + +### Automatic Upgrade +SendPortal includes an upgrade command that performs the necessary upgrade steps for you, similar to the initial setup command. To use it, run the following: + +``` +php artisan sp:upgrade +``` + +> When the command finishes, it reports that SendPortal has been upgraded to "version 2". This message has not been updated for v3, and can be safely ignored. + +### Manual Upgrade +If you prefer to perform the upgrade steps manually, you will need to do the following: + +Apply migrations: +``` +php artisan migrate +``` + +Republish assets: +``` +php artisan vendor:publish --provider='Sendportal\Base\SendportalBaseServiceProvider' --force +``` + +## Laravel Horizon +If you are using Laravel Horizon to manage redis queues, you will need to republish its assets for it to continue working with the new version of SendPortal: + +``` +php artisan horizon:publish +``` + +> Laravel Horizon stopped publishing its assets in version 5.24.4. If you have Horizon 5.24.4 or later installed (`composer show laravel/horizon`), this command only prints a warning and can be skipped. + +> For more information about Horizon, see _Running Redis Queues With Laravel Horizon_ in [Configuration & Setup](/docs/v3/getting-started/configuration-and-setup). + +## Upgrading SendPortal As A Package +If you use SendPortal as a package inside your own application, first make sure your application is running Laravel 10 or 11 and PHP 8.2 or later. + +Then update the SendPortal requirement in your `composer.json` file: + +```json +"mettle/sendportal-core": "^3.0" +``` + +Run `composer update` to install it, then apply migrations and republish SendPortal's assets: + +``` +php artisan migrate +php artisan vendor:publish --provider='Sendportal\Base\SendportalBaseServiceProvider' --force +``` + +## Notable Changes +The following functionality has changed between v2 and v3. Some of these changes were first released in later v2 versions, so you may already have them. Please check to see if any of these changes impact your use of SendPortal before you upgrade. + +### Mailgun Webhook Key +SendPortal now verifies Mailgun webhooks using your Mailgun HTTP webhook signing key, instead of your API key. If you use Mailgun, you will need to edit each Mailgun email service in SendPortal and fill in the new `Webhook Key` field. Until you do, Mailgun webhooks will be rejected and no tracking data will be recorded. See [Mailgun](/docs/v3/email-services/mailgun) for more information. + +### Postal Email Service +SendPortal can now send email through [Postal](/docs/v3/email-services/postal), an open-source mail server that you host yourself. + +### Laravel 11 And Livewire 3 +The stand-alone application has been upgraded to Laravel 11 and Livewire 3. If you have added your own code to the stand-alone application, you may need to update it for these versions. + +## Remaining On v2 +If you are not yet ready to upgrade to v3, you will need to ensure you are using a v2 tag. + +To check whether you are using a tagged version, navigate to your SendPortal installation and run the following command: + +``` +git status +``` + +If you see `HEAD detached at v2.0.4`, or another number starting `v2`, then you are using a tagged version and don't need to do anything else. + +If you see `On branch master`, then you will need to switch to a tagged version in order to avoid any risk of getting unwanted changes. + +To do this, run the following command: + +``` +git checkout v2.0.4 +``` + +If you run the `git status` command again you should now see the correct message. You will now be able to continue running v2 of SendPortal without getting unwanted changes. If you want to then go on to perform an upgrade to v3 at a later date, come back to this document and follow the upgrade instructions. + +If you use SendPortal as a package, keep your `composer.json` requirement at `"mettle/sendportal-core": "^2.0"`. diff --git a/v3/workspaces.md b/v3/workspaces.md new file mode 100644 index 0000000..bb11886 --- /dev/null +++ b/v3/workspaces.md @@ -0,0 +1,35 @@ +# Workspaces + +## Overview + +Workspaces allow you to maintain separate subscriber lists and campaigns for your projects. You may, for instance, have one workspace for your personal site's mailing list and another for your podcast. + +## View Workspaces + +Click the username displayed on the top-right side of the page, and select the _Workspaces_ item from the drop-down menu. This will display a listing of your existing workspaces and a form which allows you to create new ones. + +## Add Workspace + +A workspace only requires a name. Your user account will automatically be added to the new workspace with the administrator role. + +## Switch Workspace + +If you have multiple workspaces, you will notice a select box next to your username on the top-right hand side of the page. The default selected option is your current Workspace. You can easily switch to your other workspaces by clicking the select box and choosing your desired workspace from the list that is shown. + +## Managing Workspace Members + +> You must be the owner of the workspace to do this. + +To manage the members of your workspace, click the _Manage Users_ menu item. + +You will see a list of all the users currently in your active workspace. + +### Inviting Members +To add a new member, you must enter their email address in the field under the Invite User section, and then click _Send Invite._ + +Users who have not yet accepted the invite will be shown in a separate Invited Users section. + +To retract the invitation, click the _Retract_ button next to the user's name. + +### Removing Members +To remove a user who is already in the workspace, click the _Remove_ button next to their name in the list. diff --git a/v4/api-campaigns.md b/v4/api-campaigns.md new file mode 100644 index 0000000..ef4ab79 --- /dev/null +++ b/v4/api-campaigns.md @@ -0,0 +1,372 @@ +# Campaigns + +## Campaign Object +- data: `object` + - id: `int` (readonly) + - name: `string` + - subject: `string` + - content: `string` + - status_id: `int` (readonly) + - template_id: `int` + - email_service_id: `int` + - from_name: `string` + - from_email: `string` + - is_open_tracking: `bool` + - is_click_tracking: `bool` + - sent_count: `int` (readonly) + - open_count: `int` (readonly) + - click_count: `int` (readonly) + - send_to_all: `bool` + - tags: `array` + - save_as_draft: `bool` + - scheduled_at: `datetime` + - created_at: `datetime` (readonly) + - updated_at: `datetime` (readonly) + +## Index + +Retrieve a paginated list of all campaigns. + +### Usage + +#### Endpoint + +`GET /api/v1/campaigns` + +#### Expected Response Code +200 + +#### Response Fields + +`array` + +#### Sample Request + +``` +GET /api/v1/campaigns HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "name": "name", + "subject": "My Campaign Subject", + "content": "My Email Content", + "status_id": 1, + "template_id": 1, + "email_service_id": 1, + "from_name": "SendPortal", + "from_email": "test@sendportal.io", + "is_open_tracking": true, + "is_click_tracking": true, + "sent_count": 0, + "open_count": 0, + "click_count": 0, + "send_to_all": true, + "tags": [], + "save_as_draft": false, + "scheduled_at": "2020-07-24 08:46:54", + "created_at": "2020-07-24 08:23:38", + "updated_at": "2020-07-24 09:43:42" + } + ], + "links": { + "first": "", + "last": "", + "prev": null, + "next": null + }, + "meta": { + "current_page": 1, + "from": 1, + "last_page": 1, + "path": "", + "per_page": 25, + "to": 1, + "total": 1 + } +} +``` + +## Show + +Get details of a single campaign. + +### Usage + +#### Endpoint + +`GET /api/v1/campaigns/{campaignId}` + +#### Expected Response Code +200 + +#### Response Fields + +`Campaign Object` + +#### Sample Request + +``` +GET /api/v1/campaigns/1 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` + +#### Sample Response + +```json +{ + "data": { + "id": 1, + "name": "name", + "subject": "My Campaign Subject", + "content": "My Email Content", + "status_id": 1, + "template_id": 1, + "email_service_id": 1, + "from_name": "SendPortal", + "from_email": "test@sendportal.io", + "is_open_tracking": true, + "is_click_tracking": true, + "sent_count": 0, + "open_count": 0, + "click_count": 0, + "send_to_all": true, + "tags": [], + "save_as_draft": false, + "scheduled_at": "2020-07-24 08:46:54", + "created_at": "2020-07-24 08:23:38", + "updated_at": "2020-07-24 09:43:42" + } +} +``` + +## Store + +Create a new campaign. + +### Usage + +#### Endpoint + +`POST /api/v1/campaigns` + +#### Expected Response Code +201 + +#### Request Fields + +- name: `string` +- subject: `string` +- content: `string` +- template_id: `int` +- email_service_id: `int` +- from_name: `string` +- from_email: `string` +- is_open_tracking: `bool` (optional) +- is_click_tracking: `bool` (optional) +- send_to_all: `bool` (optional) +- tags: `array` (optional) +- save_as_draft: `bool` (optional) + +#### Response Fields + +`Campaign Object` + +#### Sample Request + +``` +POST /api/campaigns HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "subject": "My New Campaign", + "subject": "My New Campaign Subject", + "content": "My New Email Content" + "template_id": "1" + "email_service_id": "1" + "from_name": "SendPortal" + "from_email": "test@sendportal.io" + "is_open_tracking": "1" + "is_click_tracking": "1" + "send_to_all": "1" + "save_as_draft": "0" + "scheduled_at": "2020-07-24 08:46:54 +} +``` + +#### Sample Response + +```json +{ + "data": { + "id": 1, + "name": "name", + "subject": "My New Campaign Subject", + "content": "My New Email Content", + "status_id": 1, + "template_id": 1, + "email_service_id": 1, + "from_name": "SendPortal", + "from_email": "test@sendportal.io", + "is_open_tracking": true, + "is_click_tracking": true, + "sent_count": 0, + "open_count": 0, + "click_count": 0, + "send_to_all": true, + "tags": [], + "save_as_draft": false, + "scheduled_at": "2020-07-24 08:46:54", + "created_at": "2020-07-24 08:23:38", + "updated_at": "2020-07-24 08:23:38" + } +} +``` + +## Update + +Update a campaign. + +### Usage + +#### Endpoint + +`PUT /api/v1/campaigns/{campaignId}` + +#### Expected Response Code +200 + +#### Request Fields + +- name: `string` +- subject: `string` +- content: `string` +- template_id: `int` +- email_service_id: `int` +- from_name: `string` +- from_email: `string` +- is_open_tracking: `bool` (optional) +- is_click_tracking: `bool` (optional) +- send_to_all: `bool` (optional) +- tags: `array` (optional) +- save_as_draft: `bool` (optional) + +#### Response Fields + +`Campaign Object` + +#### Sample Request + +``` +PUT /api/v1/campaigns/2 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "subject": "My New Campaign Subject", + "content": "My New Email Content" +} +``` + +#### Sample Response + +```json +{ + "data": { + "id": 1, + "name": "name", + "subject": "My New Campaign Subject", + "content": "My New Email Content", + "status_id": 1, + "template_id": 1, + "email_service_id": 1, + "from_name": "SendPortal", + "from_email": "test@sendportal.io", + "is_open_tracking": true, + "is_click_tracking": true, + "sent_count": 0, + "open_count": 0, + "click_count": 0, + "send_to_all": true, + "tags": [], + "save_as_draft": false, + "scheduled_at": "2020-07-24 08:46:54", + "created_at": "2020-07-24 08:23:38", + "updated_at": "2020-07-24 09:43:42" + } +} +``` + +## Send + +Send a campaign. + +### Usage + +#### Endpoint + +`POST /api/v1/campaigns/{campaignId}/send` + +#### Expected Response Code +200 + +#### Request Fields +None + +#### Response Fields + +`Campaign Object` + +#### Sample Request + +``` +POST /api/v1/campaigns/2/send HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json +``` + +#### Sample Response + +```json +{ + "data": { + "id": 1, + "name": "name", + "subject": "My New Campaign Subject", + "content": "My New Email Content", + "status_id": 2, + "template_id": 1, + "email_service_id": 1, + "from_name": "SendPortal", + "from_email": "test@sendportal.io", + "is_open_tracking": true, + "is_click_tracking": true, + "sent_count": 0, + "open_count": 0, + "click_count": 0, + "send_to_all": true, + "tags": [], + "save_as_draft": false, + "scheduled_at": "2020-07-24 08:46:54", + "created_at": "2020-07-24 08:23:38", + "updated_at": "2020-07-24 09:43:42" + } +} +``` diff --git a/v4/api-introduction.md b/v4/api-introduction.md new file mode 100644 index 0000000..a289d4a --- /dev/null +++ b/v4/api-introduction.md @@ -0,0 +1,151 @@ +# Introduction + +## Request Requirements + +When making a request, the following requirements must be met. + +### Headers + +The following headers are required. Failure to supply these headers may lead to the API behaving in unexpected ways. + +#### Accept +An `Accept` header should be provided, with the value `application/json`. + +#### Content-Type +A `Content-Type` header should be supplied with the value `application/json`. + +## Authentication + +Authentication for the API is performed on a per-workspace basis by supplying a token with the API request. + +### Generating Tokens + +To view your existing tokens, or to generate a new token, perform the following steps for each of your Workspaces: + + - Click on your user name in the top right corner + - Select _API Tokens_ from the menu. + - You will see a list of any previously generated API tokens, including when they were generated and an option to delete them. + - To add a new token, optionally provide a description for the token, and then click _Add Token_. + +You will now be able to use the generated token to authenticate with the API for the current workspace. + +### Bearer Token + +The preferred way to authenticate with the API during a request is to use the token as a Bearer token. This is performed by supplying an `Authorization` header with the request, where the value is `Bearer {api_token}`. + +#### Example + +``` +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +``` + +### API Token Parameter + +Alternatively, you can authenticate by providing the token as a parameter when making the request. The token parameter should be keyed as `api_token` where the value is `{api_token}`. + +#### GET Example + +``` +/api/v1/ping?api_token=9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +``` + +#### POST Example + +``` +{ + "api_token": "9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ" +} +``` + +## Throttling + +By default, the API is configured to limit requests to 60 per minute. When this limit is exceeded a `429 Too Many Requests` response will be returned. + +The limit can be configured by editing the `SENDPORTAL_THROTTLE_MIDDLEWARE` key in the`.env` file. The value needs to be in the format `{number_of_requests},{every_X_minutes}`. + +For example, this would limit the API to 1000 requests every 5 minutes: + +```markdown +SENDPORTAL_THROTTLE_MIDDLEWARE=1000,5 +``` + +For more information on rate limiting see the official Laravel documentation [here](https://laravel.com/docs/master/routing#rate-limiting). + +## Testing the API + +You can easily test your API by performing a `GET` request against the `/ping` endpoint: + +`GET /api/v1/ping` + +If your API is working correctly, then this will return a `200` response with the string `ok` in the body content. Note that this endpoint does not require any authentication. + +## Errors + +### Authentication Error + +An authentication error can occur when no token is provided, or the token is invalid. + +#### Response Fields + +- message: `string` + +#### Sample Missing Token Response + +``` +{ + "message": "Unauthenticated." +} +``` + +#### Sample Invalid Token Response + +``` +{ + "message": "Unauthenticated." +} +``` + +## Validation Errors + +When data provided in a request is invalid or missing you will receive an error response explaining what is wrong with the request. + +#### Response Fields + +- message: `string` +- errors: `object` + +#### Sample Response + +``` +{ + "message": "The given data was invalid.", + "errors": { + "email": [ + "The email field is required." + ] + } +} +``` + +## Pagination + +Paginated responses follow this format: + +- data: `array` + - id: `int` + - name: `string` + - created_at: `datetime` + - updated_at: `datetime` +- links: `object` + - first: `string` + - last: `string` + - prev: `string|null` + - next: `string|null` +- meta: `object` + - current_page: `int` + - from: `int` + - last_page: `int` + - path: `string` + - per_page: `int` + - to: `int` + - total: `int` diff --git a/v4/api-subscriber-tags.md b/v4/api-subscriber-tags.md new file mode 100644 index 0000000..85b9ba7 --- /dev/null +++ b/v4/api-subscriber-tags.md @@ -0,0 +1,220 @@ +# Subscriber Tags + +## Index + +Retrieve a list of tags that have been assigned to the given subscriber. + +### Usage + +#### Endpoint + +`GET /api/v1/subscribers/{subscriberId}/tags` + +#### Expected Response Code +200 + +#### Response Fields + +- data: `array` + - id: `int` + - name: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +GET /api/v1/subscribers/1/tags HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "name": "Test Tag", + "created_at": "2020-03-23 12:44:14", + "update_at": "2020-03-23 12:44:14" + } + ] +} +``` + +## Store + +Assigns a list of tags to the given subscriber. Returns a list of tags that have been assigned to the subscriber. + +This endpoint is idempotent, meaning that tags already assigned to the subscriber will not be continuously added on repeated requests to this endpoint. However, if the intention is to supply a full list of tags that should be assigned to the given subscriber, the Update endpoint should be used instead. + +#### Endpoint + +`POST /api/v1/subscribers/{subscriberId}/tags` + +#### Expected Response Code +200 + +#### Request Fields + +- tags: `array` + +#### Response Fields + +- data: `array` + - id: `int` + - name: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +POST /api/v1/subscribers/1/tags HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "tags": [1, 2] +} +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "name": "Test Tag", + "created_at": "2020-03-23 12:44:14", + "update_at": "2020-03-23 12:44:14" + }, + { + "id": 2, + "name": "Test Tag Two", + "created_at": "2020-03-24 08:45:55", + "update_at": "2020-03-24 08:57:21" + } + ] +} +``` + +## Update + +Replaces the list of tags assigned to the given subscriber with the list supplied in the request. + +If you want to assign additional tags to the subscriber without removing existing ones, you should use the Store endpoint. If you want to remove specific tags from the subscriber, you should use the Delete endpoint. + +#### Endpoint + +`PUT /api/v1/subscribers/{subscriberId}/tags` + +#### Expected Response Code +200 + +#### Request Fields + +- tags: `array` + +#### Response Fields + +- data: `array` + - id: `int` + - name: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +PUT /api/v1/subscribers/1/tags HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "tags": [1, 2] +} +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "name": "Test Tag", + "created_at": "2020-03-23 12:44:14", + "update_at": "2020-03-23 12:44:14" + }, + { + "id": 2, + "name": "Test Tag Two", + "created_at": "2020-03-24 08:45:55", + "update_at": "2020-03-24 08:57:21" + } + ] +} +``` + +## Delete + +### Usage + +Removes the given tags from the subscriber. + +#### Endpoint + +`DELETE /api/v1/subscribers/{subscriberId}/tags` + +#### Expected Response Code +200 + +#### Request Fields + +- tags: `array` + +#### Response Fields + +- data: `array` + - id: `int` + - name: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +DELETE /api/v1/subscribers/1/tags HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "tags": [2] +} +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "name": "Test Tag", + "created_at": "2020-03-23 12:44:14", + "update_at": "2020-03-23 12:44:14" + } + ] +} +``` diff --git a/v4/api-subscribers.md b/v4/api-subscribers.md new file mode 100644 index 0000000..fe7ebb3 --- /dev/null +++ b/v4/api-subscribers.md @@ -0,0 +1,307 @@ +# Subscribers + +## Index + +Retrieve a paginated list of all subscribers. + +### Usage + +#### Endpoint + +`GET /api/v1/subscribers` + +#### Expected Response Code +200 + +#### Response Fields + +- data: `array` + - id: `int` + - first_name: `string` + - last_name: `string` + - email: `string` + - unsubscribed_at: `datetime` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +GET /api/v1/subscribers HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "first_name": "Test", + "last_name": "Subscriber", + "email": "testsubscriber@example.com", + "unsubscribed_at": null, + "created_at": "2020-03-23 13:44:09", + "updated_at": "2020-03-23 13:44:09" + }, + { + "id": 2, + "first_name": "Test", + "last_name": "Subscriber Two", + "email": "testsubscriber2@example.com", + "unsubscribed_at": "2020-08-02 08:07:08", + "created_at": "2020-03-23 13:50:39", + "updated_at": "2020-03-23 13:50:39" + } + ], + "links": { + "first": "", + "last": "", + "prev": null, + "next": null + }, + "meta": { + "current_page": 1, + "from": 1, + "last_page": 1, + "path": "", + "per_page": 25, + "to": 2, + "total": 2 + } +} +``` + +## Show + +Retrieve the details of a single subscriber, including its tags. + +### Usage + +#### Endpoint + +`GET /api/v1/subscribers/{subscriberId}` + +#### Expected Response Code +200 + +#### Response Fields + +- data: `object` + - id: `int` + - first_name: `string` + - last_name: `string` + - email: `string` + - tags: `array` + - id: `int` + - string: `string` + - created_at: `datetime` + - updated_at: `datetime` + - unsubscribed_at: `datetime` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +GET /api/v1/subscribers/1 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` + +#### Sample Response + +```json +{ + "data": { + "id": 1, + "first_name": "Test", + "last_name": "Subscriber", + "email": "testsubscriber@example.com", + "tags": [ + { + "id": 1, + "name": "Test Tag", + "created_at": "2020-03-23 12:44:14", + "update_at": "2020-03-23 12:44:14" + } + ], + "unsubscribed_at": null, + "created_at": "2020-03-23 13:44:09", + "updated_at": "2020-03-23 13:44:09" + } +} +``` + +## Store + +Create a new subscriber, optionally including tags that should be assigned to them; or update an existing subscriber based on their email address, optionally including the tags that should be assigned to them. + +This endpoint is overloaded to allow for the update of existing subscribers based on the email address provided in the request. This allows for a workflow where the caller of the API needn't know whether the subscriber is already created; this eliminates the need for developers to create different logical paths in their application and also allows subscribers to be updated without prior knowledge of their internal SendPortal ID. + +The rules for creating new subscribers or updating existing subscribers are as follows: +- If the email address is not currently used for a subscriber in the workspace, then a new subscriber will be created using the email address, name and tags provided +- If the email address is currently used by a subscriber in the workspace, then that subscriber will be updated using the name and tags provided + +### Usage + +#### Endpoint + +`POST /api/v1/subscribers` + +#### Expected Response Code +201 + +#### Request Fields + +- first_name: `string` (optional) +- last_name: `string` (optional) +- email: `string` +- unsubscribed_at: `datetime` (optional) +- tags: `array` (optional) + +#### Response Fields + +- data: `object` + - id: `int` + - first_name: `string` + - last_name: `string` + - email: `string` + - tags: `array` + - id: `int` + - name: `int` + - created_at: `datetime` + - updated_at: `datetime` + - unsubscribed_at: `datetime` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +POST /api/v1/subscribers HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "first_name": "Test", + "last_name": "Subscriber Two", + "email": "testsubscriber2@example.com", + "tags": [1] +} +``` + +#### Sample Response + +```json +{ + "data": { + "id": 2, + "first_name": "Test", + "last_name": "Subscriber two", + "email": "testsubscriber2@example.com", + "tags": [ + { + "id": 1, + "name": "Test Tag", + "created_at": "2020-03-23 12:44:14", + "update_at": "2020-03-23 12:44:14" + } + ], + "unsubscribed_at": null, + "created_at": "2020-03-24 10:43:08", + "updated_at": "2020-03-24 10:43:08" + } +} +``` + +## Update + +#### Endpoint + +`PUT /api/v1/subscribers/{subscriberId}` + +#### Expected Response Code +200 + +#### Description +Update the details of the given subscriber. + +#### Request Fields + +- first_name: `string` (optional) +- last_name: `string` (optional) +- email: `string` +- unsubscribed_at: `datetime` (optional) + +#### Response Fields + +- data: `object` + - id: `int` + - first_name: `string` + - last_name: `string` + - email: `string` + - unsubscribed_at: `datetime` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +PUT /api/v1/subscribers/2 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "first_name": "Test", + "last_name": "Subscriber Two Updated", + "email": "testsubscriber2@example.com", + "tags": [1] +} +``` + +#### Sample Response + +```json +{ + "data": { + "id": 2, + "first_name": "Test", + "last_name": "Subscriber Two Updated", + "email": "testsubscriber2@example.com", + "unsubscribed_at": null, + "created_at": "2020-03-24 10:43:08", + "updated_at": "2020-03-24 10:50:20" + } +} +``` + +## Delete + +Delete the given subscriber. + +### Usage + +#### Endpoint + +`DELETE /api/v1/subscribers/{subscriberId}` + +#### Expected Response Code +204 + +#### Sample Request + +``` +DELETE /api/v1/subscribers/2 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` diff --git a/v4/api-tag-subscribers.md b/v4/api-tag-subscribers.md new file mode 100644 index 0000000..43a158c --- /dev/null +++ b/v4/api-tag-subscribers.md @@ -0,0 +1,238 @@ +# Tag Subscribers + +## Index + +Retrieve a list of subscribers who have been assigned the given tag. + +### Usage + +#### Endpoint + +`GET /api/v1/tags/{tagId}/subscribers` + +#### Expected Response Code +200 + +#### Response Fields + +- data: `array` + - id: `int` + - first_name: `string` + - last_name: `string` + - email: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +GET /api/v1/tags/1/subscribers HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "first_name": "Test", + "last_name": "Subscriber", + "email": "testsubscriber@example.com", + "created_at": "2020-03-23 13:44:09", + "updated_at": "2020-03-23 13:44:09" + } + ] +} +``` + +## Store + +Assign the given tag to a list of subscribers. Returns a list of subscribers that have been assigned the given tag. + +This endpoint is idempotent, meaning that subscribers who have already been assigned the tag will not be continuously tagged on repeated requests to this endpoint. If the intention is to supply a full list of subscribers that should be tagged, the Update endpoint should be used instead. + +#### Endpoint + +`POST /api/v1/tags/{tagId}/subscribers` + +#### Expected Response Code +200 + +#### Request Fields + +- subscribers: `array` + +#### Response Fields + +- data: `array` + - id: `int` + - first_name: `string` + - last_name: `string` + - email: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +POST /api/v1/tags/1/subscribers HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "subscribers": [1, 2] +} +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "first_name": "Test", + "last_name": "Subscriber", + "email": "testsubscriber@example.com", + "created_at": "2020-03-23 13:44:09", + "updated_at": "2020-03-23 13:44:09" + }, + { + "id": 2, + "first_name": "Test", + "last_name": "Subscriber Two", + "email": "testsubscriber2@example.com", + "created_at": "2020-03-23 13:50:39", + "updated_at": "2020-03-23 13:50:39" + } + ] +} +``` + +## Update + +Replace all subscribers assigned a given tag with the list provided in the request. + +If you want to assign a tag to additional subscribers without removing existing ones, you should use the Store endpoint. If you want to remove the tag from specific subscribers, you should use the Delete endpoint. + +#### Endpoint + +`PUT /api/v1/tags/{tagId}/subscribers` + +#### Expected Response Code +200 + +#### Request Fields + +- subscribers: `array` + +#### Response Fields + +- data: `array` + - id: `int` + - first_name: `string` + - last_name: `string` + - email: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +PUT /api/v1/tags/1/subscribers HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "subscribers": [1, 2] +} +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "first_name": "Test", + "last_name": "Subscriber", + "email": "testsubscriber@example.com", + "created_at": "2020-03-23 13:44:09", + "updated_at": "2020-03-23 13:44:09" + }, + { + "id": 2, + "first_name": "Test", + "last_name": "Subscriber Two", + "email": "testsubscriber2@example.com", + "created_at": "2020-03-23 13:50:39", + "updated_at": "2020-03-23 13:50:39" + } + ] +} +``` + +## Delete + +Removes the given tag from the subscribers provided in the request. + +#### Endpoint + +`DELETE /api/v1/tags/{tagId}/subscribers` + +#### Expected Response Code +200 + +#### Request Fields + +- subscribers: `array` + +#### Response Fields + +- data: `array` + - id: `int` + - first_name: `string` + - last_name: `string` + - email: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +DELETE /api/v1/tags/1/subscribers HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "subscribers": [2] +} +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "first_name": "Test", + "last_name": "Subscriber", + "email": "testsubscriber@example.com", + "created_at": "2020-03-23 13:44:09", + "updated_at": "2020-03-23 13:44:09" + } + ] +} +``` diff --git a/v4/api-tags.md b/v4/api-tags.md new file mode 100644 index 0000000..0f213b8 --- /dev/null +++ b/v4/api-tags.md @@ -0,0 +1,247 @@ +# Tags + +## Index + +Retrieve a paginated list of tags. + +### Usage + +#### Endpoint + +`GET /api/v1/tags` + +#### Expected Response Code +200 + +#### Response Fields + +- data: `array` + - id: `int` + - name: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +GET /api/v1/tags HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "name": "Test Tag", + "created_at": "2020-03-23 12:44:14", + "update_at": "2020-03-23 12:44:14" + } + ], + "links": { + "first": "", + "last": "", + "prev": null, + "next": null + }, + "meta": { + "current_page": 1, + "from": 1, + "last_page": 1, + "path": "", + "per_page": 25, + "to": 1, + "total": 1 + } +} +``` + +## Show + +Get details of a single tag. + +### Usage + +#### Endpoint + +`GET /api/v1/tags/{tagId}` + +#### Expected Response Code +200 + +#### Response Fields + +- data: `object` + - id: `int` + - name: `string` + - created_at `datetime` + - updated_at `datetime` + +#### Sample Request + +``` +GET /api/v1/tags/1 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` + +#### Sample Response + +```json +{ + "data": { + "id": 1, + "name": "Test Tag", + "created_at": "2020-03-23 12:44:14", + "update_at": "2020-03-23 12:44:14" + } +} +``` + +## Store + +Create a new tag, optionally including subscribers that should have it assigned to them. + +### Usage + +#### Endpoint + +`POST /api/v1/tags` + +#### Expected Response Code +201 + +#### Request Fields + +- name: `string` +- subscribers: `array` (optional) + +#### Response Fields + +- data: `object` + - id: `int` + - name: `string` + - subscribers: `array` + - id: `int` + - first_name: `string` + - last_name: `string` + - email: `string` + - created_at: `datetime` + - updated_at: `datetime` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +POST /api/tags HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "name": "Test Tag Two", + "subscribers": [1] +} +``` + +#### Sample Response + +```json +{ + "data": { + "id": 2, + "name": "Test Tag Two", + "subscribers": [ + { + "id": 1, + "first_name": "Test", + "last_name": "Subscriber", + "email": "testsubscriber@example.com", + "created_at": "2020-03-23 13:44:09", + "updated_at": "2020-03-23 13:44:09" + } + ], + "created_at": "2020-03-24 08:45:55", + "update_at": "2020-03-24 08:45:55" + } +} +``` + +## Update + +Update the details of the given tag. If you wish to adjust the subscribers associated with the tag, see the Tag Subscribers API. + +### Usage + +#### Endpoint + +`PUT /api/v1/tags/{tagId}` + +#### Expected Response Code +200 + +#### Request Fields + +- name: `string` + +#### Response Fields + +- data: `object` + - id: `int` + - name: `string` + - created_at `datetime` + - updated_at `datetime` + +#### Sample Request + +``` +PUT /api/v1/tags/2 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "name": "Updated Tag" +} +``` + +#### Sample Response + +```json +{ + "data": { + "id": 2, + "name": "Updated Tag", + "created_at": "2020-03-24 08:45:55", + "update_at": "2020-03-24 08:57:21" + } +} +``` + +## Delete + +Delete the given tag. + +#### Endpoint + +`DELETE /api/v1/tags/{tagId}` + +#### Expected Response Code +204 + +#### Sample Request + +``` +DELETE /api/v1/tags/2 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` diff --git a/v4/api-templates.md b/v4/api-templates.md new file mode 100644 index 0000000..7939dfe --- /dev/null +++ b/v4/api-templates.md @@ -0,0 +1,241 @@ +# Templates + +## Index + +Retrieve a paginated list of all templates. + +### Usage + +#### Endpoint + +`GET /api/v1/templates` + +#### Expected Response Code +200 + +#### Response Fields + +- data: `array` + - id: `int` + - name: `string` + - content: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +GET /api/v1/templates HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` + +#### Sample Response + +```json +{ + "data": [ + { + "id": 1, + "name": "Template Name", + "content": "Template Content", + "created_at": "2020-07-17 09:48:07", + "updated_at": "2020-07-17 09:48:07" + } + ], + "links": { + "first": "http://sendportal.test/api/v1/templates?page=1", + "last": "http://sendportal.test/api/v1/templates?page=1", + "prev": null, + "next": null + }, + "meta": { + "current_page": 1, + "from": 1, + "last_page": 1, + "path": "http://sendportal.test/api/v1/templates", + "per_page": 25, + "to": 1, + "total": 1 + } +} +``` + +## Show + +Retrieve the details of a single template. + +### Usage + +#### Endpoint + +`GET /api/v1/templates/{templateId}` + +#### Expected Response Code +200 + +#### Response Fields + +- data: `object` + - id: `int` + - name: `string` + - content: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +GET /api/v1/templates/1 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` + +#### Sample Response + +```json +{ + "data": { + "id": 1, + "name": "Template Name", + "content": "Template Content", + "created_at": "2020-07-17 09:48:07", + "updated_at": "2020-07-17 09:48:07" + } +} +``` + +## Store + +Create a new template. + +### Usage + +#### Endpoint + +`POST /api/v1/templates` + +#### Expected Response Code +201 + +#### Request Fields + +- name: `string` +- content: `string` + +#### Response Fields + +- data: `object` + - id: `int` + - name: `string` + - content: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +POST /api/v1/templates HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "name": "Test Template", + "content": "Test Content" +} +``` + +#### Sample Response + +```json +{ + "data": { + "id": 3, + "name": "Test Template", + "content": "Test Content", + "created_at": "2020-07-17 09:55:10", + "updated_at": "2020-07-17 09:55:10" + } +} +``` + +## Update + +#### Endpoint + +`PUT /api/v1/templates/{templateId}` + +#### Expected Response Code +200 + +#### Description +Update the details of the given template. + +#### Request Fields + +- name: `string` +- content: `string` + +#### Response Fields + +- data: `object` + - id: `int` + - name: `string` + - content: `string` + - created_at: `datetime` + - updated_at: `datetime` + +#### Sample Request + +``` +PUT /api/v1/templates/3 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +Content-Type: application/json + +{ + "name": "Test Template", + "content": "Content" +} +``` + +#### Sample Response + +```json +{ + "data": { + "id": 3, + "name": "Test Template", + "content": "Content", + "created_at": "2020-07-17 09:55:10", + "updated_at": "2020-07-17 09:57:07" + } +} +``` + +## Delete + +Delete the given template. + +### Usage + +#### Endpoint + +`DELETE /api/v1/templates/{templateId}` + +#### Expected Response Code +204 + +#### Sample Request + +``` +DELETE /api/v1/templates/3 HTTP/1.1 +Host: sendportal.local +Authorization: Bearer 9w2fN7d4F3Banyv7gihYOWJEH6MvtYyZ +Accept: application/json +``` diff --git a/v4/campaigns.md b/v4/campaigns.md new file mode 100644 index 0000000..2c7ff91 --- /dev/null +++ b/v4/campaigns.md @@ -0,0 +1,83 @@ +# Campaigns + +## Overview + +A campaign is a set of individual email messages that are sent to your [subscribers](/docs/v4/features/subscribers). An example may be a new product launch or a regular newsletter. + +> Before creating a campaign, the following must already be configured: +> - at least one [email service](/docs/v4/email-services/introduction). +> - a list of [subscribers](/docs/v4/features/subscribers) to target with your campaign. +> - an [email template](/docs/v4/features/templates) for your campaign (optional, but recommended). + +## View Campaigns + +To view a listing of your campaigns, click the _Campaigns_ link in the sidebar. This will take you to the campaigns index page. + +## Starting New Campaigns + +Navigate to the campaigns index page and click the _New Campaign_ button. + +### Fields + +#### Campaign Name + +A friendly name for your campaign that is only used within SendPortal. It helps you properly distinguish one campaign from another. An example may be "Newsletter - May 2020". + +#### Subject + +The subject of your campaign email. This is what your mail recipients will see in their inbox, so you should try to keep it clear and concise. The Subject can include placeholders for the Subscriber's `{{first_name}}`, `{{last_name}}` and `{{email}}`. Refer to the Placeholders section of the [Templates](/docs/v4/features/templates) documentation for more information. + +#### From Name + +Also known as the the email sender name. This tells the mail recipients who sent the message. + +#### From Email + +This is what your mail recipients will see as the "from" address. This should be an address that you have ownership of and must match the settings for the selected email service. + +#### Template (optional) + +The template that you wish to use for your campaign email. + +#### Track Opens + +Determines whether or not individual email opens are tracked. If you wish to track opens, webhooks must be configured for your email service. + +#### Track Clicks + +Determines whether or not individual email clicks are tracked. If you wish to track clicks, webhooks must be configured for your email service. + +#### Content + +The body of your campaign email. This is what gets sent to your subscribers, so make it good! + +The content can include placeholders. To understand which placeholders are available to you, see the Placeholders section of the [Templates](/docs/v4/features/templates) documentation. + +Once you have filled out the form, click _Save and Continue_. Your campaign will now be marked as a **Draft**. + +## Sending Campaigns + +Campaigns are dispatched from the preview page. + +### Test Email + +It's good practice to send out a test email before you dispatch your campaign. This allows you to see how your email will appear to your recipients and offers an opportunity to pick up on any mistakes you may have made. To send a test email, simply enter an email address in the `Recipient` field and click _Send Test Email_. It will be dispatched using your configured email service. + +### Sending Options + +#### Recipients + +You can choose to dispatch your campaign to your entire subscriber list or those with specific tags. + +#### Schedule + +You can schedule your campaign to be dispatched immediately or at a later date and time. + +#### Sending Behaviour + +You can specify one of two sending behaviours for your campaign: + +- `Queue Draft` - this will generate each of your messages as a draft and requires you to dispatch each email individually from the _Messages_ section. This is a good idea if you want to double check how each email will display prior to sending, especially if you're using a complex template that may result in each email varying from subscriber to subscriber. We do not recommend this option if you have a large list of subscribers! +- `Send Automatically` - this will generate your messages and dispatch them automatically. + +> If you find that your campaigns do not send, please ensure you have configured scheduled tasks and queue workers as described in the [Additional Configuration](/docs/v4/getting-started/configuration-and-setup#additional-configuration) section. \ No newline at end of file diff --git a/v4/configuration-and-setup.md b/v4/configuration-and-setup.md new file mode 100644 index 0000000..9bfd0ae --- /dev/null +++ b/v4/configuration-and-setup.md @@ -0,0 +1,241 @@ +# Configuration & Setup + +## Introduction + +SendPortal comes packaged with a command-line setup utility that will get you up and running in a few minutes. It will attempt to automatically configure SendPortal and create a default user account and workspace for administrative purposes. + +Alternatively, you can configure SendPortal by using the Setup Wizard, or by following the Manual Configuration section below. + +- [Setup Command](#setup-command) (Recommended) +- [Setup Wizard](#setup-wizard) +- [Manual Configuration](#manual-configuration) + +Once you have successfully setup SendPortal, you must also follow the [Additional Configuration](#additional-configuration) instructions below. + +> For security reasons, we recommend setting up SendPortal as soon as possible after installation. + +## Setup Command + +In the SendPortal installation's root directory, run the following command: + +```bash +php artisan sp:install +``` + +The command will prompt you for confirmation on most steps. If this is the first time running setup for an installation, you should answer yes to the prompts in order to ensure that SendPortal is completely set up as expected. + +You can safely run the command multiple times if needed. + +Once the setup command has completed, you will need to move onto the [Additional Configuration](#additional-configuration) section in order to correctly configure how SendPortal dispatches your messages. + +## Setup Wizard + +You can also use a Setup Wizard to guide you through the installation process. If you are hosting SendPortal at `campaigns.example.com`, just point your browser to `campaigns.example.com/setup` to launch the setup process. + + > In order to launch the Setup Wizard the `.env` file must already be present in the root folder of your installation, and the encryption key must be set. See the Manual Configuration section below for more information. + + > Once your first user has been created, the Setup Wizard will no longer be available. + +Once the Setup Wizard has completed, you will need to move onto the [Additional Configuration](#additional-configuration) section in order to correctly configure how SendPortal dispatches your messages. + +## Manual Configuration + +### Creating Configuration File + +SendPortal's configuration is handled through the`.env` file. By default, this file does not exist. To create it manually, you will need to clone the included `.env.example`, rename it to `.env` and make changes as necessary. + +> Any keys that are set in the `.env` file will be used, even if they are blank. If you do not wish to actively set a key, you should remove it from your `.env` file, rather than leave it blank. + +### Key Generation + +If you did not run the setup command, or if for some reason the `APP_KEY` value is empty, you will need to create an encryption key. This is used by SendPortal to apply encryption to things like user sessions. + +To generate a new key, you should run the following command: + +```bash +php artisan key:generate +``` + +> Although key generation can be run again, this will invalidate any sessions or make any stored encrypted data inaccessible. You should not generate a new key unless absolutely necessary. + +### Base URL + +You will need to set the `APP_URL` variable in the `.env` file to the base URL for your installation of SendPortal. + +For example, if you are hosting your installation of SendPortal at `campaigns.example.com`, you will need to set the following: + +``` +APP_URL=https://campaigns.example.com +``` + +This is necessary to allow SendPortal to correctly generate unsubscribe links, or links for user registration emails. + +### Database Connection + +In order for SendPortal to connect to your database, you must set the database configuration values in the `.env` file. + +Firstly, you need to specify what type of database you are using by setting the `DB_CONNECTION` value to either `mysql` for a MySQL database or `pgsql` for a PostgreSQL database. + +Secondly, you need to set the connection details for your database installation. The following values need to be set: + +- `DB_HOST` – This is the host of your database, e.g. `127.0.0.1` for a local installation +- `DB_PORT` - The port SendPortal should use to talk to your database +- `DB_DATABASE` – The database SendPortal should use to store its data +- `DB_USERNAME` – The username SendPortal will use to authenticate itself with your database +- `DB_PASSWORD` – The password SendPortal will use to authenticate itself with your database + +### Database Migrations + +To set up the database schema, migrations must be run. Migrations are instructions an application uses to configure database schema, running in sequence from beginning to end in order to ensure that the database is set up as the application expects it to be. + +> Do not make custom modifications to the database yourself. Any database changes that SendPortal requires should be accomplished through the running of migrations. + +> Before running migrations, ensure that you have correctly configured your database connection, as schema changes will be made. + +The included command-line setup command will run migrations for you (after a prompt), but you can run migrations manually using the following command: + +```bash +php artisan migrate +``` + +### Publishing Vendor Files + +Run the following command to publish the config, views, languages and assets from SendPortal to your project: + +```bash +php artisan vendor:publish --provider=Sendportal\\Base\\SendportalBaseServiceProvider +``` + +### Workspaces & Users + +If you do not use the setup command to create a workspace and user with which to administer SendPortal, you will need to go through the web interface registration process. + +You must follow the User Management Email configuration specified in Additional Configuration in order to allow registration and user invitation in SendPortal. + +## Additional Configuration + +### Cron Jobs + +SendPortal makes use of regular background tasks and it is therefore essential to create a cron job to run every minute: + +```bash +* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1 +``` + +Please refer to the Laravel documentation on [Task Scheduling](https://laravel.com/docs/scheduling) for further information. + +### Message Sending & Queues + +SendPortal sends email messages using a queue system. The queue can be processed synchronously or asynchronously. Asynchronous queues can be handled via your primary database or via redis. + +You can specify which queue driver to use in the `QUEUE_CONNECTION` parameter in the `.env` file. This should be set to one of `sync`, `database` or `redis`, depending on your requirements. This configuration applies to all messages sent in SendPortal and cannot be changed on a per-user, per-workspace or per-provider basis. + +#### Sync + +The synchronous queue runs any queued jobs as they are requested, requiring the user to wait until the job has been completed before any further action can be taken. + +This has the advantage of being simple and requiring no additional configuration. However, as this does not scale well, this approach is only recommended for relatively small message sending requirements. + +To use the synchronous queue, you only need to set the `QUEUE_CONNECTION` to `sync`, and no further configuration is required. + +#### Database + +Processing asynchronous queues via the database is considered a middle option. Running an asynchronous queue avoids the issues that come up using a synchronous queue, as messages can be processed without blocking further action being taken by the user, and will work until all jobs are completed regardless of how long it takes. + +The advantage of using the database for running the asynchronous queue is that it requires no extra services to be run, as it uses the primary database (MySQL or Postgres) that you have already configured for SendPortal. + +The main disadvantage is that under heavy workloads the queue can negatively affect the performance of the database. + +As such, the database queue is recommended for small to medium sized mailing lists. + +To use the database driver, you must first set the `QUEUE_CONNECTION` to `database` in your `.env` file. You also need to run the following commands, which will create a new `jobs` table in your database, which that will be used to manage the queue. + +```bash +php artisan make:queue-table +php artisan migrate +``` + +#### Redis + +Redis is the recommended solution for running medium to large mailing lists. + +You will of course need an installation of redis on your server. You will then need to set the `QUEUE_CONNECTION` to `redis` and set the following configuration values in your `.env` file: + +- `REDIS_HOST` +- `REDIS_PASSWORD` +- `REDIS_PORT` + +### Running the queue without Laravel Horizon + +If you don't want to use Horizon to manage you redis queue or you're using the database driver you will have to run a [queue worker](https://laravel.com/docs/queues#running-the-queue-worker) for each queue that Sendportal uses. + +- `sendportal-message-dispatch`: dispatches messages to the email service +- `sendportal-webhook-process`: processes incoming webhooks + +``` +php artisan queue:work --queue=sendportal-message-dispatch +php artisan queue:work --queue=sendportal-webhook-process +``` + +### Running Redis Queues With Laravel Horizon + +SendPortal bundles [Laravel Horizon](https://laravel.com/docs/horizon) as an easy way to run and manage redis queues. + +Configuration for the queues necessary to run SendPortal is already included. To start processing your queue items with Horizon, you simply need to run the following command: + +``` +php artisan horizon +``` + +When using Horizon in production, you should consider using a service to ensure the queue runner restarts if it fails for any reason. The Horizon documentation has a [guide](https://laravel.com/docs/horizon#deploying-horizon) on how to use Supervisor to do this. + +#### Autoscaling + +The configuration for Horizon included with SendPortal allows autoscaling of queue workers. By default, webhooks received and messages sent via the queue each have a minimum of 2 processes running, and a maximum of 10 or 20. If these values do not suit your requirements, they can be adjusted in the `config/horizon.php` file—in particular `supervisor-2` and `supervisor-3`—using the `minProcesses` and `maxProcesses` values. + +### User Management Email + +In order to use user management functionality (for example, inviting new users or password resets) in SendPortal, it is necessary to set up an email service that SendPortal can use to send the messages. + +> If you are not going to be inviting any other users or team members to your SendPortal installation, then this section can be ignored. + +> There is no relationship between SendPortal's internal mail configuration and any email services that are configured for a workspace. + +> You will need to set `SENDPORTAL_REGISTER=true` in the `.env` file in order to use the registration and user invitation functionality. + +> By default, users are granted the ability to reset their passwords. If you wish to disable password resets, you must set `SENDPORTAL_PASSWORD_RESET=false` in the `.env` file. + +You first need to set `MAIL_MAILER` to your chosen service. The options here are `smtp`, `sendmail`, `ses`, `mailgun` and `postmark`. + +#### SMTP & Sendmail + +When using a regular SMTP provider, or sendmail, you should set the following configuration values: + +- `MAIL_HOST` – This is the host for the SMTP server +- `MAIL_PORT` – This is the port that will be used to connect to the SMTP server +- `MAIL_USERNAME` – The username used to authenticate with the SMTP server +- `MAIL_PASSWORD` – The password used to authenticate with the SMTP server +- `MAIL_FROM_ADDRESS` – The address that mail will appear to come from +- `MAIL_FROM_NAME` – The name that mail will appear to come from + +#### SES + +When using SES as your mail service, you should set the following configuration values, adding them to the `.env` file if they are not already present: + +- `AWS_ACCESS_KEY_ID` – Your AWS ID key +- `AWS_SECRET_ACCESS_KEY` – Your AWS secret key +- `AWS_DEFAULT_REGION` – Your AWS region (defaults to `us-east-1` if not included in the configuration file) + +#### Mailgun + +When using Mailgun as your mail service, you should set the following configuration values, adding them to the `.env` file if they are not already present: + +- `MAILGUN_DOMAIN` +- `MAILGUN_SECRET` +- `MAILGUN_ENDPOINT` – (defaults to `api.mailgun.net` if not included in the configuration file) + +#### Postmark + +When using Postmark as your mail service, you should set the following configuration values, adding them to the `.env` file if they are not already present: + +- `POSTMARK_TOKEN` diff --git a/v4/email-service-mailgun.md b/v4/email-service-mailgun.md new file mode 100644 index 0000000..77704d2 --- /dev/null +++ b/v4/email-service-mailgun.md @@ -0,0 +1,43 @@ +# Mailgun + +## Sending Emails + +When you first begin, Mailgun will automatically create a sandbox domain that you can use for sending, but to send using your own domain you'll need to create a custom domain. Select _Sending_ then _Domains_ from the menu on the left hand side, then click _Add New Domain_ (you'll need to add credit card details before this button appears). + +![https://sendportal.io/img/docs/providers/mailgun/mailgun-1.png](https://sendportal.io/img/docs/providers/mailgun/mailgun-1.png) + +Enter the domain name, choose whichever region makes the most sense for you and click _Add Domain_. + +![https://sendportal.io/img/docs/providers/mailgun/mailgun-2.png](https://sendportal.io/img/docs/providers/mailgun/mailgun-2.png) + +You'll now need to visit the site that you use to manage DNS for the domain provided (usually the domain registrar). The steps to do this are beyond the scope of this documentation as they vary from provider to provider, but you will need to create a `TXT` entry for the relevant domain and paste in the strings from the `Hostname` and `Enter This Value` columns. Once you've added these, click _Verify DNS Settings_. + +![https://sendportal.io/img/docs/providers/mailgun/mailgun-3.png](https://sendportal.io/img/docs/providers/mailgun/mailgun-3.png) + +You'll now need to copy the `Private API Key` from _Settings_ > _API Keys_ + +![https://sendportal.io/img/docs/providers/mailgun/mailgun-4.png](https://sendportal.io/img/docs/providers/mailgun/mailgun-4.png) + +Then paste this into the `API Key` field of your SendPortal provider configuration. You will also need to enter your sending `Domain`, and choose the `Zone` (EU or US) that matches the region you chose when adding the domain. + +![https://sendportal.io/img/docs/providers/mailgun/mailgun-5.png](https://sendportal.io/img/docs/providers/mailgun/mailgun-5.png) + +## Tracking + +To enable tracking, select _Sending_ then _Webhooks_ from the menu and click the _Add Webhook_ button. + +![https://sendportal.io/img/docs/providers/mailgun/mailgun-6.png](https://sendportal.io/img/docs/providers/mailgun/mailgun-6.png) + +The URL depends on your domain, but must end with `/api/v1/webhooks/mailgun`. For example, if SendPortal is installed at `https://campaigns.marketing.com`, then each webhook should point to `https://campaigns.marketing.com/api/v1/webhooks/mailgun`. + +![https://sendportal.io/img/docs/providers/mailgun/mailgun-7.png](https://sendportal.io/img/docs/providers/mailgun/mailgun-7.png) + +You'll need to add an entry for each type of event you want to track, but the URL is the same for each of them. + +SendPortal verifies that each webhook was sent by Mailgun, using your Mailgun `HTTP webhook signing key`. You can find this key in the Mailgun dashboard alongside your webhook and API key settings. Paste it into the `Webhook Key` field of your SendPortal provider configuration. + +> If the `Webhook Key` is missing or incorrect, SendPortal will reject Mailgun's webhooks and no tracking data will be recorded. Rejected webhooks are logged with the message `Mailgun webhook failed verification check.` + +![https://sendportal.io/img/docs/providers/mailgun/mailgun-8.png](https://sendportal.io/img/docs/providers/mailgun/mailgun-8.png) + +That's it! You're now setup to send and track e-mails using Mailgun. diff --git a/v4/email-service-mailjet.md b/v4/email-service-mailjet.md new file mode 100644 index 0000000..40eff00 --- /dev/null +++ b/v4/email-service-mailjet.md @@ -0,0 +1,31 @@ +# Mailjet + +## Sending Emails + +To send e-mails using the Mailjet provider, you will need a Mailjet account. If you don't already have an account, visit the [Mailjet website](https://www.mailjet.com/) to create one. + +> The Mailjet Free plan offers you 200 emails a day, which is enough to cover your basic needs if you are a small sender. + +When you first begin, you need to add and configure the _"From:"_ email address, also known as the _Sender Address_. Extensive documentation for this step is available [here](https://app.mailjet.com/support/how-to-add-a-sender-address,96.htm). + +Finally, in order to integrate Sendportal with Mailjet, you need to copy the `API Key` and the `API Secret Key`, which are [generated automatically](https://app.mailjet.com/account/api_keys) when your account is created, into your SendPortal provider configuration. + +![https://sendportal.io/img/docs/providers/mailjet/mailjet-1.png](https://sendportal.io/img/docs/providers/mailjet/mailjet-1.png) + +Alternatively, if you don't want to use the _Master API Key_ (which grants full access to your account), you can set up Sub-account API Keys and limit their scope. + +## Tracking + +By default the tracking options for `opens` and `clicks` are activated for all new accounts. These settings can be changed on your [Account Preferences page](https://app.mailjet.com/account/settings), but keep in mind that Sendportal will always override them with the values that you choose in the application when creating a new campaign. + +To enable tracking however you will have to set up _Webhooks_ from the [Account Information page](https://app.mailjet.com/account/triggers). + +![https://sendportal.io/img/docs/providers/mailjet/mailjet-2.png](https://sendportal.io/img/docs/providers/mailjet/mailjet-2.png) + +The URL depends on your domain, but must end with `/api/v1/webhooks/mailjet`. For example, if SendPortal is installed at `https://campaigns.marketing.com`, then each webhook should point to `https://campaigns.marketing.com/api/v1/webhooks/mailjet`. + +You'll need to add an entry for each type of event you want to track, but the URL is the same for each one of them. + +If you want to limit the number of calls, you can choose to group together events that occurred over the last second. Sendportal is able to handle both single and grouped events. + +That's it! You're now setup to send and track e-mails using Mailjet. diff --git a/v4/email-service-postal.md b/v4/email-service-postal.md new file mode 100644 index 0000000..42099fe --- /dev/null +++ b/v4/email-service-postal.md @@ -0,0 +1,35 @@ +# Postal + +[Postal](https://docs.postalserver.io) is an open-source mail server that you host yourself. + +## Sending Emails + +In your Postal web interface, open the mail server you want to send from and make sure the domain you will be sending from has been added and verified. + +Next, create an API credential for the mail server (from the _Credentials_ section, choose _API_ as the credential type) and copy the generated key. + +In SendPortal, add a new email service, choose `Postal` as the service type and enter the following: + +- `Postal Host` – the hostname of your Postal installation, without `https://`. For example, if your Postal web interface is at `https://postal.example.com`, enter `postal.example.com`. SendPortal always connects to Postal over HTTPS. +- `API Key` – the API credential key you created above. + +## Tracking + +To enable tracking, open the _Webhooks_ section of your Postal mail server and add a new webhook. + +The URL depends on your domain, but must end with `/api/v1/webhooks/postal`. For example, if SendPortal is installed at `https://campaigns.marketing.com`, then the webhook should point to `https://campaigns.marketing.com/api/v1/webhooks/postal`. + +Select only the following events, which are the ones SendPortal handles: + +- `MessageSent` – recorded as delivered +- `MessageLoaded` – recorded as opened +- `MessageLinkClicked` – recorded as clicked +- `MessageBounced` – recorded as a bounce +- `MessageDeliveryFailed` – recorded as a failure +- `MessageHeld` – recorded as a failure + +> SendPortal does not recognise other Postal events. If you send them to SendPortal, they will fail to process: as failed jobs on the `sendportal-webhook-process` queue, or as errors returned to Postal if you use the `sync` queue driver. + +Postal only sends `MessageLoaded` and `MessageLinkClicked` events when open and click tracking is set up for your mail server (Postal calls this a track domain). + +That's it! You're now setup to send and track e-mails using Postal. diff --git a/v4/email-service-postmark.md b/v4/email-service-postmark.md new file mode 100644 index 0000000..ffcec2f --- /dev/null +++ b/v4/email-service-postmark.md @@ -0,0 +1,51 @@ +# Postmark + +## Sending Email + +To send e-mails using the Postmark provider, you will need a Postmark account. If you don't already have an account, visit the [Postmark website](https://postmarkapp.com/) and click the _Start Free Trial_ button in the top right corner of the screen. + +Postmark will automatically create a server for you, called `My First Server`. Click on the server and select the _API Tokens_ tab. + +![https://sendportal.io/img/docs/providers/postmark/postmark-1.png](https://sendportal.io/img/docs/providers/postmark/postmark-1.png) + +Copy the string from `Server API tokens` and create a provider within SendPortal. + +![https://sendportal.io/img/docs/providers/postmark/postmark-2.png](https://sendportal.io/img/docs/providers/postmark/postmark-2.png) + +Next, navigate to _Sender Signatures_ and select the option to _Add Domain_. + +![https://sendportal.io/img/docs/providers/postmark/postmark-3.png](https://sendportal.io/img/docs/providers/postmark/postmark-3.png) + +Enter your domain name and click _Verify domain_. + +![https://sendportal.io/img/docs/providers/postmark/postmark-4.png](https://sendportal.io/img/docs/providers/postmark/postmark-4.png) + +You'll now need to visit the site that you use to manage DNS for the domain provided (usually the domain registrar). The steps to do this are beyond the scope of this documentation as they vary from provider to provider, but you will need to create a `TXT` entry for the relevant domain and paste in the strings from the `Hostname` and `Add this value` columns. + +![https://sendportal.io/img/docs/providers/postmark/postmark-5.png](https://sendportal.io/img/docs/providers/postmark/postmark-5.png) + +Once you've added the entry with your DNS provider, click the _Verify_ button and the DKIM status should transition to `Verified`. It can take some time to make this transition, but if it doesn't work ensure you didn't accidentally copy and paste any whitespace at the beginning or end of either string. + +![https://sendportal.io/img/docs/providers/postmark/postmark-6.png](https://sendportal.io/img/docs/providers/postmark/postmark-6.png) + +To configure e-mail tracking, select the server from the `Servers` page, select the `Transactional` (`Default Transactional Stream`) stream from the list of `Message Streams`. + +![https://sendportal.io/img/docs/providers/postmark/postmark-7.png](https://sendportal.io/img/docs/providers/postmark/postmark-7.png) + +Select the _Webhooks_ tab and click _Add webhook_. + +![https://sendportal.io/img/docs/providers/postmark/postmark-8.png](https://sendportal.io/img/docs/providers/postmark/postmark-8.png) + +The `Webhook URL` depends on your domain, but must end with `/api/v1/webhooks/postmark`. For example, if SendPortal is installed at `https://campaigns.marketing.com`, then each webhook should point to `https://campaigns.marketing.com/api/v1/webhooks/postmark`. + +![https://sendportal.io/img/docs/providers/postmark/postmark-9.png](https://sendportal.io/img/docs/providers/postmark/postmark-9.png) + +You'll also need to enable open tracking and link tracking from the stream settings. + +![https://sendportal.io/img/docs/providers/postmark/postmark-10.png](https://sendportal.io/img/docs/providers/postmark/postmark-10.png) + +That's it! You're now setup to send and track e-mails using Postmark. + +## Message Streams + +SendPortal enables the use of [Postmark message streams](https://postmarkapp.com/message-streams). You can supply this in the `Message Stream` field when adding Postmark as an Email Service. If you do not wish to use Message Streams you can leave this field blank. \ No newline at end of file diff --git a/v4/email-service-sendgrid.md b/v4/email-service-sendgrid.md new file mode 100644 index 0000000..6d424d5 --- /dev/null +++ b/v4/email-service-sendgrid.md @@ -0,0 +1,35 @@ +# Sendgrid + +## Sending Emails + +To send e-mails using the Twilio Sendgrid provider, you will need a Sendgrid account. If you don't already have one, [visit the Sendgrid website](https://sendgrid.com/) and sign up. No credit card is required and you can get up to 100 free emails per month. + +Once registered, open the `Setup Guide`, choose the `Web API` integration and select the PHP language option. + +![https://sendportal.io/img/docs/providers/sendgrid/sendgrid-1.png](https://sendportal.io/img/docs/providers/sendgrid/sendgrid-1.png) + +Generate an API key and copy it to your SendPortal configuration. + +![https://sendportal.io/img/docs/providers/sendgrid/sendgrid-2.png](https://sendportal.io/img/docs/providers/sendgrid/sendgrid-2.png) + +Check the `I've integrated the code above` box and click Next. + +Send a test e-mail from SendPortal and if it works successfully click _Verify integration_. + +## Tracking + +![https://sendportal.io/img/docs/providers/sendgrid/sendgrid-3.png](https://sendportal.io/img/docs/providers/sendgrid/sendgrid-3.png) + +Open and click tracking is enabled by default in SendGrid, but if you'd like to check the settings you'll find them under _Settings_ > _Tracking_. + +![https://sendportal.io/img/docs/providers/sendgrid/sendgrid-4.png](https://sendportal.io/img/docs/providers/sendgrid/sendgrid-4.png) + +You can also enable subscription tracking from this page. If enabled, SendGrid will automatically add unsubscribe links to the bottom of every e-mail you send. + +![https://sendportal.io/img/docs/providers/sendgrid/sendgrid-5.png](https://sendportal.io/img/docs/providers/sendgrid/sendgrid-5.png) + +To enable tracking within SendPortal you'll need to enable event notifications from _Settings_ > _Mail Settings_. + +The `HTTP POST URL` depends on your domain, but must end with `/api/v1/webhooks/sendgrid`. For example, if SendPortal is installed at `https://campaigns.marketing.com`, then each webhook should point to `https://campaigns.marketing.com/api/v1/webhooks/sendgrid`. + +That's it! You're now setup to send and track e-mails using SendGrid. diff --git a/v4/email-service-ses.md b/v4/email-service-ses.md new file mode 100644 index 0000000..0d51464 --- /dev/null +++ b/v4/email-service-ses.md @@ -0,0 +1,131 @@ +# AWS SES + +## Sending Emails + +To send e-mails using the Amazon SES provider, you will need an AWS account. If you don't already have an account, [visit the AWS website](https://aws.amazon.com/) and click the _Create an AWS Account_ button in the top right corner of the screen. + +![https://sendportal.io/img/docs/providers/aws/aws-1.png](https://sendportal.io/img/docs/providers/aws/aws-1.png) + +Be aware that you'll need to provide credit card details, even if you only intend to make use of the free tier. + +Once you've created an account, navigate to the SES (Simple Email Service) control panel by typing "simple email service" into the `Find Services` input on the AWS Management Console and clicking on the relevant option in the dropdown. + +![https://sendportal.io/img/docs/providers/aws/aws-2.png](https://sendportal.io/img/docs/providers/aws/aws-2.png) + +Click _Go to identity management_; + +![https://sendportal.io/img/docs/providers/aws/aws-3.png](https://sendportal.io/img/docs/providers/aws/aws-3.png) + +You'll need to verify the domain from which you want to send all your e-mails, so click _Verify a New Domain_. + +![https://sendportal.io/img/docs/providers/aws/aws-4.png](https://sendportal.io/img/docs/providers/aws/aws-4.png) + +At this stage we're not going to generate the DKIM settings, but this is worth doing once you move to a production environment, as it will reduce the risk of any messages you send being marked as spam. + +![https://sendportal.io/img/docs/providers/aws/aws-5.png](https://sendportal.io/img/docs/providers/aws/aws-5.png) + +Once you click _Verify This Domain_ you'll be provided with a domain verification record; + +![https://sendportal.io/img/docs/providers/aws/aws-6.png](https://sendportal.io/img/docs/providers/aws/aws-6.png) + +You'll now need to visit the site that you use to manage DNS for the domain provided (usually the domain registrar). The steps to do this are beyond the scope of this documentation as they vary from provider to provider, but you will need to create a `TXT` entry for the relevant domain and paste in the strings from the `Name` and `Value` columns. + +Once you've completed this step, click `Close`. At first the `Verification status` will be `pending verification` and `Enabled for sending` will be `No`. + +![https://sendportal.io/img/docs/providers/aws/aws-7.png](https://sendportal.io/img/docs/providers/aws/aws-7.png) + +As long as you have set the DNS entry correctly, the `Verification status` will transition to `verified` and `Enabled for Sending` will show `Yes`. You should also receive a notification via e-mail to say that the domain has been successfully verified. + +![https://sendportal.io/img/docs/providers/aws/aws-8.png](https://sendportal.io/img/docs/providers/aws/aws-8.png) + +It can take some time to make this transition, but if it doesn't work ensure you didn't accidentally copy and paste any whitespace at the beginning or end of either string. + +We also need to create a "configuration set". In order to do that, select the _Configuration Sets_ option from the side menu of the SES panel. + +![https://sendportal.io/img/docs/providers/aws/aws-9.png](https://sendportal.io/img/docs/providers/aws/aws-9.png) + +If you don't require e-mail tracking (delivery, opens etc) you only need to create the configuration set. + +![https://sendportal.io/img/docs/providers/aws/aws-10.png](https://sendportal.io/img/docs/providers/aws/aws-10.png) + +Next we need to generate an access key pair. To do this you'll need to visit the [IAM (Identity and Access Management) module of AWS](https://console.aws.amazon.com/iam/home?#/home). + +It's best to create a new IAM user, so click _Users_ and _Add user_. You can create the key pair using your root user account, but as this allows unrestricted access to the AWS console, it's a security risk if the credentials are ever compromised. + +As the user doesn't need access to the management console, you can just select the `Programmatic access` option. + +![https://sendportal.io/img/docs/providers/aws/aws-12.png](https://sendportal.io/img/docs/providers/aws/aws-12.png) + +If you don't already have any groups configured, you'll need to create one at the next step. If you only want the account to be able to send e-mails then select the `AmazonSESFullAccess` policy. + +![https://sendportal.io/img/docs/providers/aws/aws-13.png](https://sendportal.io/img/docs/providers/aws/aws-13.png) + +If you also want to enable e-mail tracking, you'll need to add the `AmazonSNSFullAccess` policy. + +![https://sendportal.io/img/docs/providers/aws/aws-14.png](https://sendportal.io/img/docs/providers/aws/aws-14.png) + +Selecting tags for the user is optional, so we're not going to enter any at this stage. Once the user has been created you should now be able to copy and paste the access key and secret access key to the SendPortal provider configuration. + +![https://sendportal.io/img/docs/providers/aws/aws-15.png](https://sendportal.io/img/docs/providers/aws/aws-15.png) + +We're now ready to configure the provider with SendPortal. + +![https://sendportal.io/img/docs/providers/aws/aws-11.png](https://sendportal.io/img/docs/providers/aws/aws-11.png) + +Congratulations! You can now send e-mails using Amazon SES. + +If you require any more information about SES, check the official [Amazon SES documentation](https://docs.aws.amazon.com/ses/index.html). + +## Tracking + +If you want to enable tracking for e-mails that you send through SES, then there are a few additional steps we need to complete. + +You'll need to configure Amazon's SNS (Simple Notification Service) in order for it to send the required webhooks to SendPortal, so navigate to the SNS page from the AWS Management Console. + +Begin by creating a topic. Select _Topics_ from the menu and click _Create topic_. Everything other than a name is optional at this time. + +![https://sendportal.io/img/docs/providers/aws/aws-16.png](https://sendportal.io/img/docs/providers/aws/aws-16.png) + +We also need to create a subscription to go along with our topic. For the Topic ARN, select the topic that you just created and select `HTTPS` as the protocol. The `Endpoint` will depend on your domain, but must end with `/api/v1/webhooks/aws`. For example, if SendPortal is installed at `https://campaigns.marketing.com`, then each webhook should point to `https://campaigns.marketing.com/api/v1/webhooks/aws`. + +Do _not_ enable `raw message delivery`, otherwise the webhooks will not be handled correctly. + +![https://sendportal.io/img/docs/providers/aws/aws-17.png](https://sendportal.io/img/docs/providers/aws/aws-17.png) + +The subscription needs to be confirmed before we can use it, but if you've supplied the correct URL for your SendPortal instance, and it's already available to the outside world, we'll do that automatically for you. You should see `Status` transition to `Confirmed`. + +![https://sendportal.io/img/docs/providers/aws/aws-18.png](https://sendportal.io/img/docs/providers/aws/aws-18.png) + +We now need to tell SES to send any e-mail tracking webhooks to this endpoint so navigate back to the SES panel (we're almost there now...). + +Select the domain that you already configured from `Domains` and open the section titled `Notifications`. + +![https://sendportal.io/img/docs/providers/aws/aws-19.png](https://sendportal.io/img/docs/providers/aws/aws-19.png) + +Click _Edit Configuration_ and select the SNS topic that you created from the dropdown for each of the notification types. + +![https://sendportal.io/img/docs/providers/aws/aws-20.png](https://sendportal.io/img/docs/providers/aws/aws-20.png) + +Finally, we need to enable the notification types for the configuration step that we created. Select the configuration set from the list and select SNS from the dropdown next to `Add Destination`. + +![https://sendportal.io/img/docs/providers/aws/aws-21.png](https://sendportal.io/img/docs/providers/aws/aws-21.png) + +Select all the notification types you wish to receive (excluding `Send` and `Rendering Failure`), and select the Topic from the list. + +![https://sendportal.io/img/docs/providers/aws/aws-22.png](https://sendportal.io/img/docs/providers/aws/aws-22.png) + +Phew! That's it folks! You should now be setup to send and track e-mails using Amazon AWS. + +![https://sendportal.io/img/docs/providers/aws/aws-23.png](https://sendportal.io/img/docs/providers/aws/aws-23.png) + +For more information on Amazon SNS, visit the [official documentation](https://docs.aws.amazon.com/sns/latest/dg/sns-getting-started.html). + +For more information on configuring SNS notifications for SES, visit the [developer guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/configure-sns-notifications.html). + +## Throttling + +AWS throttles the sending of e-mails via the SES API. Requests are limited to 1 per second by default, but you can apply to have this rate increased. + +For this reason you need to have a cache configured when using AWS. For a small mailing list the `file` driver is sufficient, but for medium or large mailing lists we recommend implementing `redis`. + +For more information on SES throttling, see the official documentation [here](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/manage-sending-quotas.html?icmpid=docs_ses_console). diff --git a/v4/email-service-smtp.md b/v4/email-service-smtp.md new file mode 100644 index 0000000..c2fb577 --- /dev/null +++ b/v4/email-service-smtp.md @@ -0,0 +1,8 @@ +# SMTP + +## Sending Emails +SendPortal can also send your campaigns via SMTP. If you select this option, then you will __not__ be able to track your campaigns - for example, deliveries, open and click tracking will not be available. + +> For production environments, we recommend using one of the API Email Services instead of SMTP. + +To create an SMTP Email Service in SendPortal, navigate to the **Email Services** tab, click the **Add Email Service** button and enter the relevant details, making sure to select **SMTP** as the type. diff --git a/v4/email-services-introduction.md b/v4/email-services-introduction.md new file mode 100644 index 0000000..c2a9934 --- /dev/null +++ b/v4/email-services-introduction.md @@ -0,0 +1,24 @@ +# Email Services + +## Introduction +SendPortal must be integrated with at least one email service provider in order for your emails to be dispatched. You can have multiple email services configured at one given time. For example, you may wish to send newsletters via SES and transactional emails via Postmark. + +> The credentials for each email service is stored in an encrypted format in the database. If the `APP_KEY` in your `.env` field is changed or lost, you will need to re-enter the credentials for each email service again. + +SendPortal currently integrates with the following email service providers: + +- [AWS SES](/docs/v4/email-services/aws) +- [Postmark](/docs/v4/email-services/postmark) +- [Sendgrid](/docs/v4/email-services/sendgrid) +- [Mailgun](/docs/v4/email-services/mailgun) +- [Mailjet](/docs/v4/email-services/mailjet) +- [Postal](/docs/v4/email-services/postal) +- [SMTP](/docs/v4/email-services/smtp) + +## Testing + +You can test if an Email Service has been correctly configured by clicking on the `Test` button in the page that lists all of your Email Services. + +![https://sendportal.io/img/docs/providers/testing.png](https://sendportal.io/img/docs/providers/testing.png) + +In order to test an Email Service you will have to provide the email address from which the email will be sent - this must be a verified email address or domain. diff --git a/v4/installation.md b/v4/installation.md new file mode 100644 index 0000000..737053f --- /dev/null +++ b/v4/installation.md @@ -0,0 +1,53 @@ +# Installation + +SendPortal can be installed as a stand-alone application (i.e. including everything you need to run SendPortal), or as a package inside an existing Laravel application. + +This page covers the stand-alone installation. If you want to install SendPortal as a package, then head over to the [Package Installation guide](/docs/v4/getting-started/package-installation). + + +## Requirements + +To run SendPortal, your environment must meet a few minimum requirements: + +- PHP ≥ 8.3 +- Git +- [Composer](https://getcomposer.org/) +- MySQL (≥ 5.7) or PostgreSQL (≥ 10) + +The stand-alone application is built on Laravel 13. + +> If you are upgrading an existing installation of SendPortal v3, see the [Upgrade Guide](/docs/v4/getting-started/upgrade-guide). + +## Installing SendPortal + +### Clone The Repository + +Clone the repository to your environment, using the following command: + +```bash +git clone --depth 1 --branch v4.0.0 https://github.com/mettle/sendportal.git +``` + +### Install Dependencies + +Once cloned, navigate to the project's root directory and run `composer install` to install SendPortal and its dependencies. + +From here, you can move on to the [Configuration & Setup](/docs/v4/getting-started/configuration-and-setup) step. + +### Webserver + +You will need to use a webserver (for example, Apache or nginx), in order to host your SendPortal installation. + +When setting up your webserver, it should be pointed to the `public` directory in order to correctly serve SendPortal. + +For example, in nginx: + +``` +server { + listen 80; + server_name campaigns.example.com; + root /var/www/campaigns.example.com/public; +} +``` + +For more detailed information, see the [Laravel deployment documentation](https://laravel.com/docs/deployment). diff --git a/v4/messages.md b/v4/messages.md new file mode 100644 index 0000000..b26b420 --- /dev/null +++ b/v4/messages.md @@ -0,0 +1,13 @@ +# Messages + +## Overview + +After a campaign has been dispatched, a message will be generated for each targeted Subscriber. A message represents a single email. You can view all of your messages by clicking the _Messages_ link in the sidebar. You will notice that there are two types of message: + +### Drafts + +A draft is a message that has been queued using the `Queue Draft` campaign sending behaviour. The _Drafts_ tab offers a listing of every drafted message and allows you to preview the final message before sending. A draft message will only be sent to the subscriber when you click on the _Send Now_ button. + +### Sent + +Similarly, the _Sent_ tab offers a listing of every message that has been dispatched. You can use this page to track and filter the status of your messages. diff --git a/v4/package-installation.md b/v4/package-installation.md new file mode 100644 index 0000000..3d7ab45 --- /dev/null +++ b/v4/package-installation.md @@ -0,0 +1,184 @@ +# Package Installation + +SendPortal can be installed as a stand-alone application (i.e. including everything you need to run SendPortal), or as a package inside an existing Laravel application. + +This page covers the Package installation. If you want to install SendPortal as a stand-alone application, then head over to the [Installation guide](/docs/v4/getting-started/installation). + +## Requirements + +- Laravel 12 or 13 +- PHP ≥ 8.3 +- MySQL (≥ 5.7) or PostgreSQL (≥ 10) + +## Installation + +To install SendPortal as a package from your command line, run the following command: + +```bash +composer require mettle/sendportal-core +``` + +Alternatively, you can add the following to your `composer.json` file: + +```json +"mettle/sendportal-core": "^4.0" +``` + +and then run `composer update` to install it. + +## Publish Assets +Run the following command to publish SendPortal's assets: + +```bash +php artisan vendor:publish --provider=Sendportal\\Base\\SendportalBaseServiceProvider +``` + +## Resolvers & Routes +SendPortal's functionality is linked to your application through the use of the `Sendportal` facade. The following sections will detail how to use this facade to get SendPortal up and running inside your application. + +> Please read all sections and decide on the relevancy to your use case. Some sections are marked as Required, and will need to be completed for a successful integration with SendPortal. Other sections are marked as Optional and may or may not be required for your own use case. + +## Workspace Resolver (Required) +In order to support multi-tenancy, SendPortal requires a "Workspace ID" for the session. Even if your host application does not use multi-tenancy, you will still need to provide a "dummy" integer. + +The Workspace ID will be used in both web and API sessions and will be stored to the database in the `workspace_id` column of tables like `sendportal_campaigns`, `sendportal_subscribers`, etc. + +> Multi-tenancy is optional. If your application is not multi-tenant, or you do not wish to use SendPortal in a multi-tenant way, you can provide a hardcoded integer value of your choice to the resolver. +> +### Registering the Workspace Resolver +Inside the `boot` method of a service provider class, provide a closure to the `Sendportal\Base\Facades\Sendportal::setCurrentWorkspaceIdResolver()` method that resolves a workspace ID. + +The return value of your closure __must__ be an integer value and __must not__ be null. + +As an example: + +```php + These routes include features like subscriber management and campaign sending, and therefore should not be exposed without some means of authentication. + +```php +// routes/web.php +Route::middleware(['auth'])->prefix('sendportal')->group(function () { + Sendportal::webRoutes(); +}); +``` + +### Public Web Routes (Required) +To provide access to features such as unsubscribing from a mailing list and viewing subscriber emails in the browser, you must register SendPortal's public web routes. + +Inside a routes file, call the `Sendportal\Base\Facades\Sendportal::publicWebRoutes()` method. + +> These routes include publically accessible features such as unsubscribing from a mailing list, and therefore should _not_ be guarded by authentication. + +```php +// routes/web.php +Sendportal::publicWebRoutes(); +``` + +### Public API Routes (Required) +To handle campaign tracking (i.e. webhooks from your email service provider), you must register SendPortal's public API routes. + +> New Laravel applications do not include a `routes/api.php` file. You can create one by running `php artisan install:api`, or register the routes in any other routes file. Webhooks are sent without a CSRF token, so the routes should not use the `web` middleware group. + +Inside a routes file, call the `Sendportal\Base\Facades\Sendportal::publicApiRoutes()` method. + +> These routes include webhooks that are needed to track campaigns inside SendPortal, and must be accessible to your email service. + +```php +// routes/api.php +Sendportal::publicApiRoutes(); +``` + +### Set Up SendPortal API Routes (Optional) +If you wish to provide access to SendPortal's API, then you must register SendPortal's API routes. + +Inside a routes file, call the `Sendportal\Base\Facades\Sendportal::apiRoutes()` method. + +> These routes include features like subscriber and campaign management, and should not be exposed to the internet without some means of authentication. + +The example below uses [Laravel Sanctum](https://laravel.com/docs/sanctum); use whichever guard your application uses to authenticate API requests. + +```php +// routes/api.php +Route::middleware(['auth:sanctum'])->group(function() { + Sendportal::apiRoutes(); +}); +``` + +## Set Up Sidebar HTML Content Resolver (Optional) +Optionally, you can provide content to the sidebar navigation in SendPortal. This allows you to inject new menu items that can link to resources you build yourself. This is helpful for providing access to functionality like user management inside SendPortal. + +### Registering Sidebar HTML Resolver +Inside the `boot` method of a service provider class, provide a closure to the `Sendportal\Base\Facades\Sendportal::setSidebarHtmlContentResolver()` method. + +The return value of your closure __must__ be a string value or null. You should ideally provide HTML, as it will be rendered directly into the view as provided. + +As an example: + +```php + public function boot(): void + { + Sendportal::setSidebarHtmlContentResolver(function () { + return view('layouts.sidebar.manageUsersMenuItem')->render(); + }); + } +``` + +## Set Up Header HTML Content Resolver (Optional) +You can optionally provide content to the header in SendPortal. This allows you to inject resources that you build yourself. This is helpful for providing functionality like user profile management or workspace switchers inside SendPortal. + +### Registering Header HTML Resolver +Inside the `boot` method of a service provider class, provide a closure to the `Sendportal\Base\Facades\Sendportal::setHeaderHtmlContentResolver()` method. + +The return value of your closure __must__ be a string value or null. You should ideally provide HTML, as it will be rendered directly into the view as provided. + +As an example: + +```php + public function boot(): void + { + Sendportal::setHeaderHtmlContentResolver(function () { + return view('layouts.header.userManagementHeader')->render(); + }); + } +``` + +## Further Examples +To see an example of SendPortal used as a fully multi-tenant application, please see our own [host application](https://github.com/mettle/sendportal), which takes advantage of all the integration features available in the SendPortal package. + +In particular, see the `App\Providers\AppServiceProvider` class for registering resolvers, and see the `routes/web.php` and `routes/api.php` files for registering routes. \ No newline at end of file diff --git a/v4/quickstart.md b/v4/quickstart.md new file mode 100644 index 0000000..cc7f1e2 --- /dev/null +++ b/v4/quickstart.md @@ -0,0 +1,79 @@ +# Quickstart Guide + +## Introduction + +The following guide will walk you through sending out your first campaign. It assumes that you have: + +- successfully installed and configured SendPortal; and +- created your first user; and +- that you are logged in. + +For more detailed instructions for each feature, refer to the relevant article in the Features section. + +## Create An Email Service + +SendPortal integrates with [Mailgun](/docs/v4/email-services/mailgun), [Mailjet](/docs/v4/email-services/mailjet), [Amazon SES](/docs/v4/email-services/aws), [Sendgrid](/docs/v4/email-services/sendgrid), [Postmark](/docs/v4/email-services/postmark) and [SMTP](/docs/v4/email-services/smtp). + +Click on the _Email Services_ link in the sidebar and then click the _Add Email Service_ button. + +The required information will vary between each service. Full instructions on how to set up each service can be found in the [email service documentation](/docs/v4/email-services/introduction). + +## Add Or Import Subscribers + +In order for SendPortal to send emails, you need to set up lists of [subscribers](/docs/v4/features/subscribers). These subscribers can be added in 3 ways: + +- Manually +- Through the API +- CSV Import + +To add a single subscriber manually, click the _Subscribers_ link in the sidebar, and then click _New Subscriber_. You will need to provide an email address for the subscriber and ensure that `Subscribed` is checked. Once you are happy with your subscriber, click _Save_. + +Refer to the [subscriber documentation](/docs/v4/features/subscribers) for more information. + +## Create A Template + +A [template](/docs/v4/features/templates) allows you to create consistent branding for all your campaigns, so that you only need to create new content for each campaign. + +> Campaigns can be created without a template as well, if desired. + +To create a template, click on the _Templates_ link in the sidebar and then click the _Add Template_ button. Templates should contain valid HTML and must include at least a `{{content}}` tag. + +Refer to the [templates documentation](/docs/v4/features/templates) for more information. + +## Create A Campaign + +To create a campaign, click on the _Campaigns_ link in the sidebar and then click the _New Campaign_ button. + +If you have created a template in the previous step, you can select it here. The content will be placed into the `{{content}}` tag in the template. + +Refer to the [campaign documentation](/docs/v4/features/campaigns) for more information. + +## Send The Campaign + +After your campaign has been created, you will have a chance to review it before sending. + +### Test Email + +You can test the email by entering an address in the Test Email section and clicking _Send Test Email_. + +### Recipients + +Specify who the campaign should be sent to; either **All Subscribers,** or those with one or more **tags** assigned to them. + +### Schedule + +Choose to dispatch the campaign immediately or at a specific and date and time. + +### Sending Behaviour + +Finally, you can choose to save each individual email as a draft message, or send it out immediately. + +Choosing to save the message as a draft means you will will need to review and send each individual message before it is sent out. + +Choosing to send the messages immediately will dispatch each message as soon as SendPortal is ready to do so. + +## Next Steps + +This covers the basics of how to set up your email services, templates, subscribers and campaigns. + +For more detailed information on each of these items, please refer to the relevant Features section of the documentation. diff --git a/v4/subscribers.md b/v4/subscribers.md new file mode 100644 index 0000000..fca649f --- /dev/null +++ b/v4/subscribers.md @@ -0,0 +1,113 @@ +# Subscribers + +## Overview + +Subscribers represent individuals that are available to you when sending out campaigns. Subscribers can optionally be assigned one or more [tags](/docs/v4/features/tags). + +> The email address for each Subscriber must be unique in each [workspace](/docs/v4/features/workspaces). + +## View Subscribers + +To view a list of your subscribers, click the _Subscribers_ link in the sidebar. This will take you to the subscribers index page. + +### Subscriber Table + +Subscribers are listed in a paginated table, where each row represents a different subscriber. + +### Filtering Subscribers + +The following filtering options are available: + +- Subscriber Email and/or Name. Partial matches will also be returned. +- Subscriber Subscription Status + +## Viewing an Individual Subscriber + +To view an individual subscriber, head to the subscribers index page and click on the subscriber's email in the table that's presented to you. + +### Subscriber Details + +Each subscriber's email, first name, last name and subscription status is displayed when viewing an individual subscriber. + +In addition, if the subscriber has been assigned any [tags](/docs/v4/features/tags) then these are also displayed. + +### Messages + +If any messages have been sent to the subscriber, they will be listed in the Messages section. + +## Adding and Updating Subscribers + +Subscribers can be added and updated in the following ways: + +* manually through the application interface +* via a CSV upload +* programmatically using the API. + +### Application Interface + +#### Adding A Subscriber + +Navigate to the subscribers index page and click _New Subscriber_. + +#### Updating A Subscriber + +Click the _Edit_ button on the subscriber's row in the list. + +The following information can be saved for each Subscriber: + +- email +- first name (optional) +- last name (optional) +- subscription status +- tags (optional) + +### CSV Imports + +SendPortal allows you to easily import multiple subscribers via a CSV upload. This can be used to create new subscribers and update existing ones. + +To import subscribers via CSV, click the _Subscribers_ link in the sidebar. From there, click the overflow menu button and then _Import Subscribers_. + +#### CSV File Format + +The following fields are available in the CSV. + +- `id` (optional) - The ID field is only used when updating existing subscribers, and must be set if you wish to update the subscriber's email address. The `id` field should be present but left empty when importing new subscribers. +- `email` (required) - A unique email address for the subscriber. Note that if the email address is already present in SendPortal, the row will instead update the `first_name` and `last_name` of the existing subscriber. +- `first_name` (optional) - The subscriber's first name. +- `last_name` (optional) - The subscriber's last name. + +Additionally, the **first row** of the CSV **must** be a header row, where each column is the name as listed above, and must be provided in the order above. + +Each row after the header row must represent a single subscriber. Optional fields can be left empty. + +> The import page shows an example of a CSV file, with the header and a row. + +#### Uploading The File + +To upload the file, click the _Browse_ button on the file selector, and choose the file you wish to upload. When you have selected your file, you can click the _Upload_ button to begin the upload process. + +#### Tags + +Tags can be assigned to subscribers by using the `Tags` selector. If you have any tags available, you can pick which ones to assign here. Note that the chosen tags will apply to all subscribers uploaded in the chosen CSV. It is currently not possible to specify separate tags for each individual subscriber. + +### API + +Refer to the [subscribers API documentation](/docs/v4/api/subscribers) for information on how to manage subscribers through the API. + +## Exporting Subscribers + +To export subscribers, click the _Subscribers_ link in the sidebar. From there, click the overflow menu button and then _Export Subscribers_. + +## Unsubscribing + +### Manually Unsubscribing + +To manually unsubscribe a subscriber, you should go to the subscriber edit page, uncheck `Subscribed` and then click _Save_. + +### Unsubscribe Links + +SendPortal allows automatic injection of unsubscribe links in your templates and campaigns. See the [templates](/docs/v4/features/templates) section for more information on how this works. + +When a user clicks on the unsubscribe link, they will be sent to a web page asking them to confirm that they want to be unsubscribed. + +If they decide to unsubscribe, they will be shown a confirmation message that they have been unsubscribed, along with the option to resubscribe. diff --git a/v4/tags.md b/v4/tags.md new file mode 100644 index 0000000..891368d --- /dev/null +++ b/v4/tags.md @@ -0,0 +1,45 @@ +# Tags + +## Overview + +Tags offer a convenient means of grouping subscribers together. For example, you may have one tag for your monthly newsletter and another for customers that want to be notified of a new product launch. + +Tags are completely optional; a subscriber can be assigned one tag, multiple tags, or none at all. + +## View Tags + +To view a listing of your tags, click the _Tags_ button on the subscribers index page. This will take you to the tags index. + +## Adding and Updating Tags + +Tags can be added in the following ways: + +- through the application interface +- via the API + +### Application Interface + +To add a new tag through the application interface, navigate to the tags index page and click _New Tag_. + +To edit a tag, click the _Edit_ button on the tag listing. + +Tags are very simple and only require a `Name`, which must be unique across all tags in the current workspace. + +### API + +Refer to the [Tags API Documentation](/docs/v4/api/tags) for information on how to manage tags through the API. + +## Deleting Tags + +Tags can be deleted in the following ways: + +- through the application interface +- via the API + +### Application Interface + +To delete a tag through the application interface, navigate to the tags index page. Once there, find your target tag in the listing and click the _Delete_ button. + +### API + +Refer to the [Tags API Documentation](/docs/v4/api/tags) for information on how to delete tags through the API. \ No newline at end of file diff --git a/v4/templates.md b/v4/templates.md new file mode 100644 index 0000000..3f0834c --- /dev/null +++ b/v4/templates.md @@ -0,0 +1,56 @@ +# Templates + +## Overview + +Templates make it easy for you to reuse content and designs across your campaigns. You may, for example, want to create a template for your monthly newsletter. This would make it easier for you to keep the design of your emails consistent and reduce the effort that's needed to prepare your newsletter each month. Our dynamic templating system also allows you to insert subscriber details into your templates, such as their name. + +## View Templates + +To view a listing of your templates, click the _Templates_ link in the sidebar. This will take you to the templates index page. + +## Creating and Updating Templates + +To add a template, navigate to the templates index page and then click the _New Template_ button. + +A template requires the following: + +- A name +- Contents of the template + +The contents of a template must be valid HTML. + +Additionally, the template **must** include a `{{content}}` placeholder to inject the contents of a campaign. To set the placeholder, you should include the following at the desired place in your template: + +``` +{{content}} +``` + +When sending a [campaign](/docs/v4/features/campaigns), the campaign's content will be substituted for this placeholder. + +### Placeholders + +As well as the required `{{content}}` placeholder, the following placeholders are available in templates: + +- `{{email}}` – The subscriber's email +- `{{first_name}}` – The subscriber's first name (if set) +- `{{last_name}}` – The subscriber's last name (if set) +- `{{unsubscribe_url}}` – A URL generated to allow the subscriber to unsubscribe +- `{{webview_url}}` – A URL generated to allow the subscriber to view the message in a web view + +### URL Placeholders + +The URL placeholders (`{{unsubscribe_url}}` and `{{webview_url}}`) generate the URL only; they do not create a clickable link such as an HTML anchor tag. + +This means that simply placing one of these placeholders in the template will result in the URL being displayed as plain text. You are required to create an appropriate anchor tag and include the URL as the `href` property if you wish to create clickable links. For example: + +```html +Click here to unsubscribe +``` + +### Inline CSS Styles + +Although external stylesheets and embedded styles (i.e. including the styles in the `` of an HTML page) work well for web pages, many email clients will ignore or strip out these elements. + +SendPortal automatically inlines all of your CSS styles when the emails are dispatched. You can therefore continue to use the convenience of CSS and/or embedded styles in your templates, but still ensure that your emails display correctly for your subscribers. + +If your template contains CSS and inline styles, the inline styles will be retained, and the CSS and/or embedded styles will be added to the existing inline styles. diff --git a/v4/upgrade-guide.md b/v4/upgrade-guide.md new file mode 100644 index 0000000..a692d07 --- /dev/null +++ b/v4/upgrade-guide.md @@ -0,0 +1,132 @@ +# SendPortal v3 to v4 Upgrade Guide + +SendPortal v4 upgrades SendPortal to Laravel 13. Laravel 11, which SendPortal v3 is built on, no longer receives security fixes, and recent versions of Composer will refuse to install or update SendPortal v3's dependencies because of known security advisories. We recommend upgrading to v4 as soon as possible. + +> Please see the "Notable Changes" section at the bottom of this document for a list of changes that could impact your use of SendPortal after the upgrade. + +> If you do not wish to upgrade to v4, please see the "Remaining On v3" section at the bottom of this document. + +## Requirements + +> We recommend creating a database backup for your SendPortal installation before proceeding with the v4 upgrade. + +### PHP +SendPortal v4 bumps the minimum supported version of PHP from 8.2 to __8.3__. + +> We recommend using the latest version of PHP possible, as older versions no longer receive bug fixes or security updates. + +### Laravel +The stand-alone SendPortal application now runs on Laravel 13. + +If you use SendPortal as a package, your application must be running Laravel 12 or 13 before you upgrade. See the [Laravel upgrade guide](https://laravel.com/docs/13.x/upgrade) for details on upgrading your application. + +### Database +SendPortal v4 requires PostgreSQL 10 or later, up from 9.4. The minimum MySQL version is unchanged at 5.7. + +## Updating To The Latest Version +To get the latest version of SendPortal, you must checkout the new v4 tag. + +First, ensure you are in your SendPortal installation directory: + +``` +cd /path/to/sendportal +``` + +Then use git to fetch and checkout the `v4.0.0` tag: + +``` +git fetch --tags +git checkout v4.0.0 +``` + +> If you have made your own changes to SendPortal's files, git may not be able to checkout the new tag until you commit or stash those changes, and you may need to resolve conflicts with the changes made for Laravel 13. + +Finally, update SendPortal's dependencies: + +``` +composer update +``` + +## Performing The Upgrade +Once you have the new version of SendPortal on your system, you will need to perform an upgrade to ensure your installation works correctly. + +### Automatic Upgrade +SendPortal includes an upgrade command that performs the necessary upgrade steps for you, similar to the initial setup command. To use it, run the following: + +``` +php artisan sp:upgrade +``` + +> When the command finishes, it reports that SendPortal has been upgraded to "version 2". This message has not been updated for v4, and can be safely ignored. + +### Manual Upgrade +If you prefer to perform the upgrade steps manually, you will need to do the following: + +Apply migrations: +``` +php artisan migrate +``` + +Republish assets: +``` +php artisan vendor:publish --provider='Sendportal\Base\SendportalBaseServiceProvider' --force +``` + +> SendPortal v4 does not add any database migrations, but running `migrate` will apply any migrations from v3 that you have not yet run. + +## Laravel Horizon +Laravel Horizon no longer publishes its assets, so you no longer need to run `php artisan horizon:publish` after upgrading. If your deployment scripts still run it, you can remove it; it only prints a warning. + +## Upgrading SendPortal As A Package +If you use SendPortal as a package inside your own application, first make sure your application is running Laravel 12 or 13 and PHP 8.3 or later. + +Then update the SendPortal requirement in your `composer.json` file: + +```json +"mettle/sendportal-core": "^4.0" +``` + +Run `composer update` to install it, then apply migrations and republish SendPortal's assets: + +``` +php artisan migrate +php artisan vendor:publish --provider='Sendportal\Base\SendportalBaseServiceProvider' --force +``` + +## Notable Changes +The following functionality has changed between v3 and v4. Please check to see if any of these changes impact your use of SendPortal before you upgrade. + +### CSRF Middleware Renamed +Laravel 13 renamed its CSRF protection middleware from `VerifyCsrfToken` to `PreventRequestForgery`. The stand-alone application's `App\Http\Middleware\VerifyCsrfToken` class has been renamed to `App\Http\Middleware\PreventRequestForgery` to match. + +If you added any URIs to the `$except` list of the old middleware, move them to the new `app/Http/Middleware/PreventRequestForgery.php` file. + +The new middleware also accepts form submissions that the browser marks as coming from the same origin, in addition to checking the CSRF token. + +### Laravel 13 +The stand-alone application has been upgraded from Laravel 11 to Laravel 13. If you have added your own code to the stand-alone application, see the Laravel [12](https://laravel.com/docs/12.x/upgrade) and [13](https://laravel.com/docs/13.x/upgrade) upgrade guides for changes that may affect it. + +## Remaining On v3 +If you are not yet ready to upgrade to v4, you will need to ensure you are using a v3 tag. + +> Laravel 11 stopped receiving security fixes in March 2026. Remaining on v3 leaves your installation exposed to known vulnerabilities, and Composer will refuse to install or update its dependencies unless you disable its security checks. + +To check whether you are using a tagged version, navigate to your SendPortal installation and run the following command: + +``` +git status +``` + +If you see `HEAD detached at v3.0.1`, or another number starting `v3`, then you are using a tagged version and don't need to do anything else. + +If you see `On branch master`, then you will need to switch to a tagged version in order to avoid any risk of getting unwanted changes. + +To do this, run the following command: + +``` +git checkout v3.0.1 +``` + +If you run the `git status` command again you should now see the correct message. You will now be able to continue running v3 of SendPortal without getting unwanted changes. If you want to then go on to perform an upgrade to v4 at a later date, come back to this document and follow the upgrade instructions. + +If you use SendPortal as a package, keep your `composer.json` requirement at `"mettle/sendportal-core": "^3.0"`. diff --git a/v4/workspaces.md b/v4/workspaces.md new file mode 100644 index 0000000..bb11886 --- /dev/null +++ b/v4/workspaces.md @@ -0,0 +1,35 @@ +# Workspaces + +## Overview + +Workspaces allow you to maintain separate subscriber lists and campaigns for your projects. You may, for instance, have one workspace for your personal site's mailing list and another for your podcast. + +## View Workspaces + +Click the username displayed on the top-right side of the page, and select the _Workspaces_ item from the drop-down menu. This will display a listing of your existing workspaces and a form which allows you to create new ones. + +## Add Workspace + +A workspace only requires a name. Your user account will automatically be added to the new workspace with the administrator role. + +## Switch Workspace + +If you have multiple workspaces, you will notice a select box next to your username on the top-right hand side of the page. The default selected option is your current Workspace. You can easily switch to your other workspaces by clicking the select box and choosing your desired workspace from the list that is shown. + +## Managing Workspace Members + +> You must be the owner of the workspace to do this. + +To manage the members of your workspace, click the _Manage Users_ menu item. + +You will see a list of all the users currently in your active workspace. + +### Inviting Members +To add a new member, you must enter their email address in the field under the Invite User section, and then click _Send Invite._ + +Users who have not yet accepted the invite will be shown in a separate Invited Users section. + +To retract the invitation, click the _Retract_ button next to the user's name. + +### Removing Members +To remove a user who is already in the workspace, click the _Remove_ button next to their name in the list.