Skip to main content

Pingram Gateway for Hermes

Chat with your Hermes agent over SMS and Email, routed through Pingram. Text or email your Pingram number/address and the agent replies on the same channel — including inbound MMS images.

This is a self-contained Hermes platform plugin: one plugin.yaml + one adapter.py. It registers a single pingram platform that serves both channels (Pingram uses one API key for both). Inbound messages are received by polling Pingram's logs API on a timer — there's no public endpoint or webhook to set up. It requires zero changes to Hermes core.

flowchart LR
  human["Human"] -->|"SMS / Email"| pingram["Pingram"]
  pingram -->|"logs.getLogs (polled every N seconds)"| adapter["PingramAdapter"]
  adapter -->|"MessageEvent"| agent["Hermes agent session"]
  agent -->|"reply"| send["PingramAdapter.send()"]
  send -->|"Pingram Python SDK"| pingram
  pingram -->|"SMS / Email"| human

The channel is encoded in the Hermes chat_id prefix: sms:{phone} and email:{thread}.

Requirements

  • A running Hermes agent (hermes gateway).
  • A Pingram account with an API key (pingram_sk_...). Sender identity is optional — Pingram provides a default SMS number and email sender, so you can start without provisioning your own.
  • Python packages pingram-python (the Pingram SDK) and aiohttp available to the gateway — installed automatically with the pip install method below.
  • No public host, tunnel, or webhook registration is required — the adapter polls Pingram for new messages.

Install

Option 1: pip (recommended)

Install from PyPI into the same environment as your Hermes agent. This pulls in the Pingram SDK and aiohttp for you, and registers the plugin via its hermes_agent.plugins entry point — the gateway discovers it automatically.

pip install hermes-pingram-gateway

hermes-pingram-gateway is the plugin. It depends on pingram-python, the Pingram SDK (imported as pingram); the two are separate packages.

Option 2: hermes plugins install (from source)

hermes plugins install pingram-io/hermes-gateway
pip install pingram-python aiohttp

hermes plugins install git-clones this repo into ~/.hermes/plugins/pingram/ and the gateway auto-discovers it on next start. Because it doesn't install Python dependencies, you install the SDK and aiohttp yourself.

Configure

You can configure Pingram two ways — a guided wizard (recommended) or by editing env vars directly.

Option A: Guided setup (recommended)

First enable the plugin so it shows up in the gateway setup menu, then run the wizard:

hermes plugins enable pingram
hermes setup gateway

Select Pingram from the messaging-platforms list and answer the prompts. The wizard asks for your region and API key, then which channel(s) to enable (SMS, Email, or both). Sender details are optional — for SMS you can use your Pingram account's default number or pin a specific one; for Email you can set a display name and address or leave them blank for Pingram's defaults. It also asks how often to poll for new messages (default every 15s). It writes everything to ~/.hermes/.env for you — then just start the gateway.

Option B: Manual env vars

Set these in your Hermes env (~/.hermes/.env) — see .env.example for the full list. At minimum you need the API key, region, and channel(s).

# Required:
PINGRAM_API_KEY=pingram_sk_...
PINGRAM_REGION=us                        # us | eu | ca — must match your Pingram account's region
PINGRAM_CHANNELS=sms,email               # which channels to enable: sms, email, or both

# Senders are OPTIONAL — leave them unset to use Pingram's account defaults.
#PINGRAM_FROM_SMS=+15551234567           # pin a specific verified Pingram number
#PINGRAM_FROM_EMAIL=agent@yourdomain.com # sender address on a verified domain
#PINGRAM_FROM_NAME=My Bot                # email sender display name

# Inbound polling cadence (optional):
#PINGRAM_POLL_INTERVAL=15                # seconds between polls (default 15)
#PINGRAM_POLL_LIMIT=50                   # max messages fetched per poll page

# Everything below is optional (defaults shown / commented out):
#PINGRAM_ALLOWED_USERS=+15559876543,you@yourdomain.com
#PINGRAM_ALLOW_ALL_USERS=false           # dev only

Set PINGRAM_REGION to the region your Pingram account lives in (us, eu, or ca). It selects the API endpoint — the wrong value will fail to send.

Enable the platform in your gateway config (~/.hermes/config.yaml):

plugins:
  enabled: true
gateway:
  platforms:
    pingram:
      enabled: true

Start (or restart) the gateway:

hermes gateway restart

How inbound messages are received

The adapter polls Pingram's logs API (logs.getLogs) every PINGRAM_POLL_INTERVAL seconds (default 15). Each cycle it fetches the newest messages, keeps the ones that arrived since the last poll, and dispatches the inbound SMS/Email to your agent. A startup watermark means only messages that arrive after the gateway starts are delivered (history isn't replayed), and per-message tracking-id deduplication guards against reprocessing.

This means there's nothing to expose to the internet — no public host, tunnel, or webhook registration. Just start the gateway and text/email your Pingram number/address.

Trade-offs versus a push webhook:

  • Latency: delivery is delayed by up to one poll interval (lower the interval for snappier replies, at the cost of more API calls).
  • Email attachments: inbound email attachments are not downloaded — Pingram's logs API returns attachment metadata only, not content. Inbound SMS/MMS images are fetched and passed to the agent for vision. Outbound file replies (the agent attaching files to an email) still work.

SMS quickstart

  1. Set PINGRAM_API_KEY, PINGRAM_REGION, and PINGRAM_CHANNELS=sms. Optionally pin PINGRAM_FROM_SMS; otherwise Pingram uses your default number.
  2. Add your phone to PINGRAM_ALLOWED_USERS.
  3. Start the gateway.
  4. Text your Pingram number — the agent replies by SMS within a poll interval. Inbound MMS images are passed to the agent for vision.

Email quickstart

  1. Set PINGRAM_API_KEY, PINGRAM_REGION, and PINGRAM_CHANNELS=email. Optionally set PINGRAM_FROM_EMAIL / PINGRAM_FROM_NAME; otherwise Pingram uses its default sender.
  2. Add your email to PINGRAM_ALLOWED_USERS.
  3. Start the gateway.
  4. Email your Pingram address — the agent replies in-thread (Re: subject) within a poll interval. The agent's file replies are sent back as email attachments. (Inbound email attachments are not downloaded when polling.)

Security

The logs API is account-scoped and authenticated by PINGRAM_API_KEY, so there's no inbound endpoint to authenticate. Defense-in-depth still applies to who the agent will talk to:

  • User allowlist — an inbound message's from must be in PINGRAM_ALLOWED_USERS unless PINGRAM_ALLOW_ALL_USERS=true. With no allowlist and false, inbound messages are ignored.
  • Deduplication — each message is processed once (keyed on its Pingram tracking id), even if it appears across consecutive polls.
  • Message bodies and secrets are never logged; phone numbers/emails are redacted.

Configuration reference

Env var Required Default Description
PINGRAM_API_KEY yes — Pingram API key (pingram_sk_...).
PINGRAM_REGION yes us us | eu | ca. Must match your account's region — selects the API endpoint.
PINGRAM_CHANNELS yes* inferred Channels to enable: sms, email, or sms,email. *Optional only if a sender is set (legacy inference).
PINGRAM_FROM_SMS no Pingram default Pin a specific verified SMS sender number (E.164). Blank → account default.
PINGRAM_FROM_EMAIL no noreply@pingram.io Email sender address (verified domain). Blank → Pingram default.
PINGRAM_FROM_NAME no Pingram default Email sender display name (e.g. My Bot).
PINGRAM_POLL_INTERVAL no 15 Seconds between polls for new inbound messages.
PINGRAM_POLL_LIMIT no 50 Max messages fetched per poll page.
PINGRAM_ALLOWED_USERS no — Allowed phones/emails (CSV).
PINGRAM_ALLOW_ALL_USERS no false Allow everyone (dev only).
PINGRAM_NOTIFICATION_TYPE no hermes_agent_reply Pingram notification type.

Known limitations (V1)

  • Inbound email attachments: not downloaded. Polling Pingram's logs API returns attachment metadata only, not content. Inbound SMS/MMS images and outbound email file replies work fully.
  • Latency: inbound delivery is delayed by up to one PINGRAM_POLL_INTERVAL.
  • Outbound SMS MMS: the Pingram send API has no SMS media field, so the agent can't attach locally-generated images to an SMS. If a public image URL is available it's appended to the message text; otherwise a short note is added.

License

MIT — see LICENSE if present, otherwise this plugin is provided under the MIT License.

Metadata

Release files for hermes-pingram-gateway 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for hermes-pingram-gateway 1.0.0
File Size Uploaded
hermes_pingram_gateway-1.0.0.tar.gz 19.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for hermes-pingram-gateway 1.0.0
File Interpreter ABI Platform
hermes_pingram_gateway-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 36.9 kB

Release files / hermes_pingram_gateway-1.0.0.tar.gz

Download URL hermes_pingram_gateway-1.0.0.tar.gz
Size 19.8 kB
Tags Source
SHA-256 checksum
How to use checksums
2536278c68cc81442bb07f4873bd94fe24ff963c10bbbf5e6fb74c7aaaa72722
BLAKE2b-256 checksum
How to use checksums
a4e40f45c7825441a54ad17b4afce6d068aa9ec3337c55d90df84e3a665bef0f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 2, 2026.

Transparency log

Release files / hermes_pingram_gateway-1.0.0-py3-none-any.whl

Download URL hermes_pingram_gateway-1.0.0-py3-none-any.whl
Size 17.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bfba86f466c2f691cb01e0f8198a93810995b9b07189a87b99515586a8bd2baf
BLAKE2b-256 checksum
How to use checksums
74285c768377d2b984eda9e063e5cc7d067a26fe4d5f6acb24f65b021593465d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 2, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page