
Security News
Nx npm Packages Compromised in Supply Chain Attack Weaponizing AI CLI Tools
Malicious Nx npm versions stole secrets and wallet info using AI CLI tools; Socket’s AI scanner detected the supply chain attack and flagged the malware.
Official Ruby SDK for the PossiNote API - Send SMS, emails, and schedule messages with ease.
Add this line to your application's Gemfile:
gem 'possinote'
And then execute:
$ bundle install
Or install it yourself as:
$ gem install possinote
require 'possinote'
# Initialize the client with your API key
client = Possinote::Client.new(api_key: 'your_api_key_here')
# Send a single SMS
response = client.sms.send(
to: '+233244123456',
message: 'Hello from Possinote!',
sender_id: 'YourSenderID'
)
# Send a single email
response = client.email.send(
recipient: 'user@example.com',
subject: 'Welcome to Possinote',
content: '<h1>Hello!</h1><p>Welcome to our platform.</p>',
sender_name: 'Your Company'
)
All API requests require authentication using your API key:
client = Possinote::Client.new(api_key: 'your_api_key_here')
response = client.sms.send(
to: '+233244123456',
message: 'Your message here',
sender_id: 'YourSenderID'
)
# Response
{
"success" => true,
"data" => {
"message_id" => "msg_123456789",
"to" => "+233244123456",
"status" => "queued",
"cost" => 1.0,
"created_at" => "2025-08-11T11:30:00Z"
}
}
response = client.sms.send_bulk(
sender_id: 'YourSenderID',
messages: [
{ to: '+233244123456', message: 'Message 1' },
{ to: '+233244123457', message: 'Message 2' }
]
)
# Response
{
"success" => true,
"data" => {
"batch_id" => "batch_123456789",
"total_messages" => 2,
"successful" => 2,
"failed" => 0,
"total_cost" => 2.0,
"messages" => [
{ "message_id" => "msg_1", "to" => "+233244123456", "status" => "queued" },
{ "message_id" => "msg_2", "to" => "+233244123457", "status" => "queued" }
]
}
}
response = client.sms.schedule(
recipient: '+233244123456',
message: 'Scheduled message',
sender_id: 'YourSenderID',
scheduled_at: '2025-08-11T12:00:00Z'
)
# Response
{
"success" => true,
"data" => {
"id" => "schedule_123456789",
"recipient" => "+233244123456",
"message" => "Scheduled message",
"scheduled_at" => "2025-08-11T12:00:00Z",
"status" => "pending",
"cost" => "1.0"
}
}
response = client.sms.schedule_bulk(
sender_id: 'YourSenderID',
messages: [
{ recipient: '+233244123456', message: 'Scheduled message 1' },
{ recipient: '+233244123457', message: 'Scheduled message 2' }
],
scheduled_at: '2025-08-11T12:00:00Z'
)
# Response
{
"success" => true,
"data" => {
"batch_id" => "batch_123456789",
"scheduled_count" => 2,
"total_cost" => 2.0,
"scheduled_at" => "2025-08-11T12:00:00Z",
"messages" => [
{ "id" => "schedule_1", "recipient" => "+233244123456", "status" => "pending" },
{ "id" => "schedule_2", "recipient" => "+233244123457", "status" => "pending" }
]
}
}
response = client.email.send(
recipient: 'user@example.com',
subject: 'Welcome Email',
content: '<h1>Welcome!</h1><p>Thank you for joining us.</p>',
sender_name: 'Your Company'
)
# Response
{
"success" => true,
"message" => "Email queued for delivery",
"recipient" => "user@example.com",
"message_id" => "email_123456789"
}
response = client.email.send_bulk(
subject: 'Newsletter',
content: '<h1>Newsletter</h1><p>This is our monthly newsletter.</p>',
recipients: ['user1@example.com', 'user2@example.com'],
sender_name: 'Your Company'
)
# Response
{
"success" => true,
"message" => "Bulk emails queued for delivery",
"queued_count" => 2,
"total_count" => 2,
"batch_id" => "batch_123456789",
"emails" => [
{ "message_id" => "email_1", "recipient" => "user1@example.com", "status" => "queued" },
{ "message_id" => "email_2", "recipient" => "user2@example.com", "status" => "queued" }
]
}
response = client.scheduling.schedule_email(
recipient: 'user@example.com',
subject: 'Scheduled Email',
content: '<h1>Scheduled Content</h1>',
scheduled_at: '2025-08-11T12:00:00Z',
sender_name: 'Your Company'
)
# Response
{
"success" => true,
"data" => {
"id" => "email_schedule_123456789",
"recipient" => "user@example.com",
"subject" => "Scheduled Email",
"scheduled_at" => "2025-08-11T12:00:00Z",
"status" => "pending",
"cost" => "1.0"
}
}
response = client.scheduling.schedule_multiple_emails([
{
recipient: 'user1@example.com',
subject: 'Personalized Email 1',
content: '<h1>Hello User 1!</h1>',
scheduled_at: '2025-08-11T12:00:00Z',
sender_name: 'Your Company'
},
{
recipient: 'user2@example.com',
subject: 'Personalized Email 2',
content: '<h1>Hello User 2!</h1>',
scheduled_at: '2025-08-11T12:00:00Z',
sender_name: 'Your Company'
}
])
# Response
{
"success" => true,
"data" => {
"batch_id" => "batch_123456789",
"total_scheduled" => 2,
"total_cost" => 2.0,
"scheduled_emails" => [
{ "id" => "email_1", "recipient" => "user1@example.com", "status" => "pending" },
{ "id" => "email_2", "recipient" => "user2@example.com", "status" => "pending" }
]
}
}
# Gemfile
gem 'possinote'
# config/initializers/possinote.rb
require 'possinote'
# Initialize the client
POSSINOTE_CLIENT = Possinote::Client.new(
api_key: Rails.application.credentials.possinote[:api_key]
)
# app/controllers/notifications_controller.rb
class NotificationsController < ApplicationController
def send_sms
begin
response = POSSINOTE_CLIENT.sms.send(
to: params[:phone_number],
message: params[:message],
sender_id: 'YourBrand'
)
if response['success']
render json: { message: 'SMS sent successfully', data: response['data'] }
else
render json: { error: 'Failed to send SMS' }, status: :unprocessable_entity
end
rescue Possinote::AuthenticationError => e
render json: { error: 'Authentication failed' }, status: :unauthorized
rescue Possinote::PaymentRequiredError => e
render json: { error: 'Insufficient credits' }, status: :payment_required
rescue Possinote::ValidationError => e
render json: { error: e.message }, status: :bad_request
rescue => e
render json: { error: 'An error occurred' }, status: :internal_server_error
end
end
def send_bulk_sms
begin
response = POSSINOTE_CLIENT.sms.send_bulk(
sender_id: 'YourBrand',
messages: params[:messages] # Array of { to: phone, message: text }
)
render json: { message: 'Bulk SMS sent', data: response['data'] }
rescue => e
render json: { error: e.message }, status: :internal_server_error
end
end
def send_email
begin
response = POSSINOTE_CLIENT.email.send(
recipient: params[:email],
subject: params[:subject],
content: params[:content],
sender_name: 'Your Company'
)
render json: { message: 'Email sent successfully', data: response }
rescue => e
render json: { error: e.message }, status: :internal_server_error
end
end
end
# app/services/notification_service.rb
class NotificationService
def initialize
@client = POSSINOTE_CLIENT
end
def send_welcome_sms(user)
@client.sms.send(
to: user.phone_number,
message: "Welcome #{user.name}! Your account has been created successfully.",
sender_id: 'YourBrand'
)
end
def send_password_reset_email(user, reset_token)
@client.email.send(
recipient: user.email,
subject: 'Password Reset Request',
content: generate_password_reset_html(user, reset_token),
sender_name: 'Your Company'
)
end
def send_bulk_newsletter(users, newsletter_content)
messages = users.map do |user|
{
to: user.phone_number,
message: "Newsletter: #{newsletter_content[:title]}"
}
end
@client.sms.send_bulk(
sender_id: 'YourBrand',
messages: messages
)
end
def schedule_reminder_email(user, appointment)
@client.scheduling.schedule_email(
recipient: user.email,
subject: 'Appointment Reminder',
content: generate_appointment_reminder_html(appointment),
scheduled_at: appointment.datetime - 1.hour,
sender_name: 'Your Company'
)
end
private
def generate_password_reset_html(user, token)
<<~HTML
<h1>Password Reset Request</h1>
<p>Hello #{user.name},</p>
<p>You requested a password reset. Click the link below to reset your password:</p>
<a href="#{Rails.application.routes.url_helpers.reset_password_url(token: token)}">
Reset Password
</a>
<p>This link will expire in 1 hour.</p>
HTML
end
def generate_appointment_reminder_html(appointment)
<<~HTML
<h1>Appointment Reminder</h1>
<p>Hello #{appointment.user.name},</p>
<p>This is a reminder for your appointment:</p>
<ul>
<li><strong>Date:</strong> #{appointment.datetime.strftime('%B %d, %Y')}</li>
<li><strong>Time:</strong> #{appointment.datetime.strftime('%I:%M %p')}</li>
<li><strong>Location:</strong> #{appointment.location}</li>
</ul>
HTML
end
end
# app/jobs/send_notification_job.rb
class SendNotificationJob < ApplicationJob
queue_as :default
def perform(notification_type, user_id, options = {})
user = User.find(user_id)
service = NotificationService.new
case notification_type
when 'welcome_sms'
service.send_welcome_sms(user)
when 'password_reset_email'
service.send_password_reset_email(user, options[:reset_token])
when 'bulk_newsletter'
service.send_bulk_newsletter(options[:users], options[:content])
when 'scheduled_reminder'
service.schedule_reminder_email(user, options[:appointment])
end
end
end
# Usage in controller
SendNotificationJob.perform_later('welcome_sms', user.id)
SendNotificationJob.perform_later('password_reset_email', user.id, reset_token: token)
# config/credentials.yml.enc
possinote:
api_key: your_api_key_here
# Or use environment variables
# config/initializers/possinote.rb
POSSINOTE_CLIENT = Possinote::Client.new(
api_key: ENV['POSSINOTE_API_KEY']
)
The SDK provides specific exception classes for different error types:
begin
response = client.sms.send(to: '+233244123456', message: 'Hello', sender_id: 'SenderID')
rescue Possinote::AuthenticationError => e
puts "Authentication failed: #{e.message}"
rescue Possinote::PaymentRequiredError => e
puts "Payment required: #{e.message}"
rescue Possinote::RateLimitError => e
puts "Rate limit exceeded: #{e.message}"
rescue Possinote::ValidationError => e
puts "Validation error: #{e.message}"
rescue Possinote::APIError => e
puts "API error: #{e.message}"
end
Possinote::AuthenticationError
- Invalid API key (401)Possinote::PaymentRequiredError
- Insufficient credits (402)Possinote::RateLimitError
- Rate limit exceeded (429)Possinote::ValidationError
- Invalid request data (400)Possinote::APIError
- Other API errorsThe SDK uses the production API by default. For testing, you can modify the base URL:
# In lib/possinote/client.rb
BASE_URI = 'https://notifyapi.possitech.net/api/v1'
HTTP requests use default timeout settings. You can customize them in the client:
# The SDK uses HTTParty defaults
# You can modify timeout settings in lib/possinote/client.rb
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/possitech/possinote-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
The gem is available as open source under the terms of the MIT License.
For support, email support@possitech.net or visit our documentation.
FAQs
Unknown package
We found that possinote demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Malicious Nx npm versions stole secrets and wallet info using AI CLI tools; Socket’s AI scanner detected the supply chain attack and flagged the malware.
Security News
CISA’s 2025 draft SBOM guidance adds new fields like hashes, licenses, and tool metadata to make software inventories more actionable.
Security News
A clarification on our recent research investigating 60 malicious Ruby gems.