Coding Projects

How to Send a Ruby Push Notification to Your iPhone

Send a Ruby push notification to your iPhone in about five lines: no gems, no APNs setup. Plain Net::HTTP, plus a Rails example.

Red and black halftone illustration: a faceted cut stone held in fine tweezers under a jeweller lamp (TheNotificationApp)

Your Sidekiq job swallowed an exception at 6 a.m. and quietly retried itself into oblivion. You found out hours later, when a customer asked why their export came back empty. A push to your phone would have told you in seconds, and from Ruby, sending one is about five lines.

Ruby gives you plenty of ways to catch a failure: a Sidekiq retry, a rescue block, an exception tracker filling up with rows. Every one of them assumes you're at a screen, looking. A push notification doesn't wait for you to check: it lands on your phone the second the rescue block runs, whether you're at your desk or on the train.

Most "send a notification from Ruby" tutorials reach for a gem, an APNs certificate, a provider account, and an OAuth dance. For a personal ping, the job died, the deploy shipped, someone signed up: that's wildly out of proportion. TheNotificationApp is a single HTTP endpoint: one POST, one header, a JSON body. No gem in your Gemfile, no certificate to rotate. Here's the whole thing.

What you need

  • A TheNotificationApp account and an app_key: the free tier is enough to follow along.
  • The iOS app installed and signed in with the same account, so the notification has somewhere to land.
  • Ruby. That's it. Net::HTTP ships in the standard library, so there are no gems to install.

The five lines

Drop your key in an environment variable and fire a POST at the endpoint. This is the entire integration:

require "net/http"
require "json"

Net::HTTP.post(
  URI("https://thenotification.app/api/sendNotification"),
  { title: "Deploy finished", body: "Build #4821 is live in production." }.to_json,
  "app_key" => ENV["NOTIFICATION_APP_KEY"], "Content-Type" => "application/json"
)

Run it with NOTIFICATION_APP_KEY=your_key ruby notify.rb and your phone buzzes. The .to_json is the part that's easy to forget. The API expects a JSON body, and a Ruby hash isn't one until you convert it. Skip it and you'll get a 400 back.

A method you'll actually reuse

An inline call is fine for a throwaway script. For anything you'll call from more than one place, wrap it once so the URL, headers, and error handling live in a single spot:

require "net/http"
require "json"
require "uri"

def notify(title, body, **extra)
  uri = URI("https://thenotification.app/api/sendNotification")
  payload = { title: title, body: body }.merge(extra).to_json

  res = Net::HTTP.post(
    uri,
    payload,
    "app_key" => ENV.fetch("NOTIFICATION_APP_KEY"),
    "Content-Type" => "application/json"
  )

  raise "Notification failed: #{res.code} #{res.body}" unless res.code == "200"

  JSON.parse(res.body)
end

notify("Backup done", "Nightly dump finished in 4m 12s.")

Two small choices earn their keep here. ENV.fetch raises if the key is missing, so a misconfigured server fails loudly instead of sending nothing. And raising on any non-200 means a broken notification can't hide a broken job: the failure surfaces where you'd expect it.

Sending more than text

Two optional fields are worth knowing about:

  • link: a URL the notification opens when you tap it. Point it straight at the PR, the dashboard, or the failed run.
  • image: an image URL shown inside the notification.

Because notify takes keyword args, you pass them through without touching the method:

notify(
  "New signup",
  "alex@example.com just created an account.",
  link: "https://dash.example.com/users/8842"
)

Both are optional, leave them off and you get a plain title-and-body push. There are only four fields total, so there's nothing to memorize; the API reference has the complete list.

Wiring it into a Rails app

Calling a push notification API from Rails works best where you already care that something went sideways: a background job or a rescue block. A job is the cleanest home, because failures there are otherwise invisible until someone complains:

class ImportJob < ApplicationJob
  rescue_from(StandardError) do |error|
    notify("Import job failed", "#{self.class.name}: #{error.message}")
    raise error
  end

  def perform(csv_path)
    Importer.run(csv_path)
  end
end

The rescue_from fires the push and then re-raises, so Active Job (or Sidekiq) still records the failure and retries as configured, you get the alert and keep the normal behavior. Put notify in a small lib/ file or a service object so jobs, mailers, and rake tasks can all reach it. One thing to avoid: don't block a web request on the call. An HTTP round-trip to an external service in the request cycle adds latency to a page load, push it to a job instead.

Reading the response

A success comes back as JSON you can log or ignore:

{ "success": true, "message": "Notification sent to 1 device(s)", "devices_sent": 1, "failed_count": 0 }

The codes worth handling: 401 means a missing or wrong app_key, 400 means you left out title or body, and 429 means you hit your limit: the body tells you the cap:

{ "success": false, "error": "Monthly message limit exceeded", "limit": 100, "received": 101 }

The notify method above turns each of those into a raised exception, which inside a job means they land in the same place as every other failure you already track. Worth a glance at the rate limits page if you're wiring this into something busy.

The honest tradeoff

Fair warning on the numbers: the free tier is 100 notifications total, not per month, total, across up to 3 apps. That's plenty to wire up "deploy shipped" and "nightly job died" and then forget the whole thing exists. But point it at a busy Sidekiq queue that retries on every blip and you'll burn through 100 in an afternoon. Pro is $2.99/month for 1,000 notifications a month and up to 10 apps. Either way, put a threshold in front of anything chatty, notify on the failure, not on each of 500 retries.

That's the whole thing

No gem, no certificate, one POST you can read at a glance. If you've got a Ruby script or a Rails job whose failures you currently hear about from users first, this is worth a look. You can start free at thenotification.app. Working in more than one language? The Python and Node.js versions are the same idea with different syntax.

New to this? Start with what a push notification API actually is.