Skip to main content

Pulse

Pulse is a self-hosted REST API to send notifications. It wraps the Symfony Notifier component behind a simple JSON API, so any application, regardless of its language, can send chat, email, push and SMS messages through a single HTTP call. Pulse is built on Fusio, which gives you authentication, scopes, rate limiting, logging, an API documentation and a developer portal out of the box.

Features​

  • One API, four channels: send messages via POST /chat, POST /email, POST /push and POST /sms
  • Many providers: switch between providers like Twilio, Slack, Discord or SendGrid by changing a DSN, without changing your client code
  • Provider specific options: pass provider specific options (i.e. Slack blocks, Telegram parse mode, Expo push token) through the options field
  • Access control: every channel is protected by its own scope (chat, email, push, sms), so you can decide which app is allowed to use which channel
  • Events: every sent message triggers an event (chat.sent, email.sent, push.sent, sms.sent) in the CloudEvents format, which you can subscribe to via webhooks
  • Docker ready: an official image is available at fusio/pulse

Supported providers​

The following Symfony bridges are installed by default:

ChannelProviders
ChatSlack, Discord, Microsoft Teams, Telegram, Google Chat, Fake Chat (for testing)
EmailSendGrid, Mailgun, Amazon SES, Postmark, Resend
PushExpo, OneSignal, Novu
SMSTwilio, Vonage, Sinch, Infobip, Telnyx

Additional providers can be added by requiring the fitting Symfony bridge, i.e. composer require symfony/mattermost-notifier.

Configuration​

Each channel is configured through a DSN environment variable in the .env file. A channel which has no DSN configured returns a 501 Not Implemented response.

CHAT_DSN="slack://TOKEN@default?channel=CHANNEL"
EMAIL_DSN="sendgrid://KEY@default"
PUSH_DSN="expo://TOKEN@default"
SMS_DSN="twilio://SID:TOKEN@default?from=FROM"

The exact DSN format of each provider is described in the Symfony Notifier documentation.

Besides the DSNs you need to configure the regular Fusio settings like the database connection (FUSIO_CONNECTION), the base url (FUSIO_URL) and the project key (FUSIO_PROJECT_KEY).

Installation​

Docker​

docker run -d -p 8080:80 \
-e FUSIO_PROJECT_KEY=change-me \
-e FUSIO_URL=http://localhost:8080 \
-e FUSIO_CONNECTION="pdo-mysql://user:password@db/pulse" \
-e SMS_DSN="twilio://SID:TOKEN@default?from=FROM" \
fusio/pulse

Manual​

git clone https://github.com/apioo/fusio-pulse.git
cd fusio-pulse
composer install

# adjust the .env file, then
php bin/fusio migrate
php bin/fusio adduser --role=1 --username=admin --email=admin@example.com --password=change-me
php bin/fusio login --username=admin --password=change-me
php bin/fusio deploy

Point the document root of your web server to the public/ folder.

Usage​

Authentication​

All endpoints require an access token which contains the scope of the channel. You can obtain a token through the Fusio authorization endpoint, i.e. via the client credentials grant using your app key and secret:

curl -X POST http://localhost:8080/authorization/token \
-u "APP_KEY:APP_SECRET" \
-d "grant_type=client_credentials&scope=chat,email,push,sms"

Send the token in the Authorization header of every request:

Authorization: Bearer [TOKEN]

Chat​

POST /chat (scope chat)

curl -X POST http://localhost:8080/chat \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{
"subject": "Deployment finished successfully",
"options": {
"channel": "#releases"
}
}'
FieldTypeDescription
subjectstringRequired The message text
optionsmapProvider specific options

Email​

POST /email (scope email)

curl -X POST http://localhost:8080/email \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{
"from": "noreply@example.com",
"to": ["jane@example.com"],
"subject": "Welcome",
"text": "Hello Jane, welcome on board!",
"html": "<p>Hello Jane, welcome on board!</p>"
}'
FieldTypeDescription
fromstringSender address
tostring[]Recipient addresses
replyTostring[]Reply-To addresses
ccstring[]CC addresses
bccstring[]BCC addresses
priorityintegerPriority from 1 (highest) to 5 (lowest)
subjectstringSubject of the email
textstringPlain text body
htmlstringHTML body

Push​

POST /push (scope push)

curl -X POST http://localhost:8080/push \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{
"subject": "New message",
"content": "You have received a new message",
"options": {
"to": "ExponentPushToken[xxxxxxxx]"
}
}'
FieldTypeDescription
subjectstringRequired Title of the notification
contentstringRequired Body of the notification
optionsmapProvider specific options, i.e. the device token to for Expo

SMS​

POST /sms (scope sms)

curl -X POST http://localhost:8080/sms \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{
"phone": "+4917612345678",
"subject": "Your verification code is 123456"
}'
FieldTypeDescription
phonestringRequired Phone number of the recipient
subjectstringRequired The message text
fromstringSender, overrides the from configured in the DSN
optionsmapProvider specific options

Response​

All endpoints return the same response format:

{
"success": true,
"message": "SMS successfully sent"
}
StatusDescription
200The message was sent
400The request payload is invalid
401The access token is missing or has not the needed scope
500The provider could not send the message
501The channel has no DSN configured

Events​

After a message was sent, Pulse dispatches one of the following events. You can register a webhook in the Fusio backend to get notified, i.e. to keep an audit log of all sent messages.

EventDescription
chat.sentA chat message was sent
email.sentAn email message was sent
push.sentA push message was sent
sms.sentA SMS message was sent

Development​

The API is defined in configuration files which are deployed through php bin/fusio deploy:

  • resources/operation.yaml - the available endpoints (see resources/operations/)
  • resources/scope.yaml - the scopes of each channel
  • resources/role.yaml - the roles and their scopes
  • resources/event.yaml - the events which are dispatched
  • resources/typeschema.json - the TypeSchema specification of the request and response models

The business logic lives in src/: the Action classes handle the HTTP request and delegate to the Service classes which build the Symfony Notifier message and send it through the configured transport. The classes in src/Model are generated from the TypeSchema specification.

Run the static analysis with:

vendor/bin/phpstan analyse

License​

This project is licensed under the Apache License 2.0, see the LICENSE file for details.