I wanted to be able to tell Hermes, "Send this email tomorrow at 9 a.m." So I built hermes-sendgrid, an open-source plugin that connects Hermes Agent to Twilio SendGrid.
SMTP would be enough if sending were the only job. I wanted Hermes to handle the steps around it too.
"Pause that email." "Use this template for the weekly report." "Is this recipient suppressed?" "How did this week's messages perform?" I made each task an explicit operation so Hermes can validate a request before sending and stop a scheduled message when needed.
The project started with this line:
Give Hermes an email superpower with SendGrid: send, schedule, cancel, template, and understand deliverability.
Six tools in v0.1
- send plain-text, HTML, or multipart email;
- send data through a SendGrid Dynamic Template;
- schedule one email up to 72 hours ahead;
- inspect, pause, resume, or cancel a scheduled batch;
- check recipient health using read-only suppression signals; and
- retrieve category statistics by purpose.
I used the SendGrid Web API because it covers the work before and after submission as well as the send itself.
With Dynamic Templates, Hermes supplies data such as a name or summary. SendGrid manages the subject, HTML, branding, and layout. Hermes does not have to generate fresh HTML for every message.
For scheduled mail, Ruby converts the ISO 8601 datetime from Hermes into the Unix timestamp required by the Mail Send API. The plugin rejects past dates and anything more than 72 hours away before calling SendGrid. It asks SendGrid for an official batch_id, which can later be used to pause, resume, or cancel the batch. SendGrid allows pause and cancel operations until ten minutes before send_at.
Dry-run uses SendGrid Sandbox Mode. The plugin sends the payload to SendGrid and waits for 200 OK rather than only checking it locally. Sandbox Mode validates the request without delivering the email.
Slack can call it without keeping a service running
A Slack request to Hermes can be as direct as:
Send exactly one email through SendGrid. Use the recipient, subject, and body below. After sending, report the status and mail ID.
Hermes owns the Slack integration. hermes-sendgrid contains no Slack bot or webhook receiver. Its runtime path is:
Hermes Agent
β Python Plugin Adapter
β JSON via stdin/stdout
β Ruby CLI
β HTTPS
β Twilio SendGrid Web API v3
The plugin uses no daemon, background service, listening socket, or MCP server. Hermes starts the Ruby process when it calls a tool. Ruby makes the required SendGrid API calls and exits.
There is no resident process. Ruby runs only when Hermes calls a tool.
SendGrid behavior lives in Ruby. Python defines the Hermes schemas, registers the tools, starts the executable, writes JSON to stdin, and parses JSON from stdout. The subprocess does not use a shell, and neither the API key nor the message body appears in argv. The Ruby core uses standard libraries such as Net::HTTP, so installation does not require Bundler.
Ruby enforces the safety rules
The tool descriptions warn Hermes about accidental sends, but the enforcement happens in Ruby.
Each invocation accepts one recipient. There is no CC, BCC, or bulk-send path. Operators can configure an exact recipient allowlist. Real sends are limited to 20 attempts per rolling hour by default, with a file lock to handle concurrent calls. The local state contains timestamps only, not recipients, subjects, bodies, or template data.
The documentation recommends Restricted Access API keys with only the permissions needed for each tool. A key with Mail Send permission can still send normal and template messages without suppression or statistics access.
email_health checks bounces, invalid emails, spam reports, global unsubscribes, and group unsubscribes through SendGrid's suppression APIs. It is read-only. The plugin cannot delete a suppression or bypass an unsubscribe. If the API key lacks permission for one check, the result is sendable: null with that check marked unknown, not sendable: true.
Open and click tracking are off by default because they have privacy implications. Categories are limited to stable values such as hermes-agent and follow_up. SendGrid's Categories guidance says categories are not the place for personal or high-cardinality data. Recipients, subjects, prompts, and conversation text never go into categories or custom arguments.
Testing with a real account
The automated tests use a fake HTTP client and never contact SendGrid. The Ruby suite has 59 tests and 249 assertions. The Python adapter has 14 tests. GitHub Actions passes on Ruby 3.3, Ruby 4.0, and Python 3.11.
I tested Sandbox Mode and a real send separately.
The sandbox request went through the actual Python adapter and Ruby CLI. SendGrid returned 200 OK, the plugin reported validated, and no message was delivered.
I then sent one sample through the same path. SendGrid returned HTTP 202, and the plugin reported accepted with a randomly generated mail ID. The recipient later confirmed that the message arrived.
accepted is not delivered. HTTP 202 only confirms that SendGrid accepted the request. The recipient confirmation was a separate check; the plugin did not track final delivery.
v0.1 does not track final delivery
v0.1 has no Event Webhook or Inbound Parse. It cannot automatically check the final delivery state of an individual message. That is the tradeoff for keeping the plugin on demand instead of running a resident server.
Possible next steps include optional Email Activity queries for supported accounts, readable aliases for Dynamic Template IDs, purpose-specific ASM groups, and local metadata for scheduled messages. Message bodies will remain out of local storage, and those additions would use the same no-server, request-response design.
Mapping SendGrid features to Hermes tools
With dry-run, scheduled management, and recipient health alongside send_email, each result can report the action Hermes took and any check it could not complete.
SendGrid provides Sandbox Mode, Dynamic Templates, scheduled-send management, Suppressions, and Categories Statistics. Exposing them as separate Hermes tools adds validation before a send and aggregate reporting afterward to ordinary submission.
hermes-sendgrid is available under the MIT License. With a verified SendGrid sender and an API key limited to the required permissions, install it with:
hermes plugins install geeknees/hermes-sendgrid --enable
Repository: github.com/geeknees/hermes-sendgrid



Top comments (0)