Hermes Agent platform plugin: chat with your agent over Pingram SMS and Email.
Project description
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) andaiohttpavailable to the gateway — installed automatically with thepip installmethod 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-gatewayis the plugin. It depends onpingram-python, the Pingram SDK (imported aspingram); 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_REGIONto the region your Pingram account lives in (us,eu, orca). 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
- Set
PINGRAM_API_KEY,PINGRAM_REGION, andPINGRAM_CHANNELS=sms. Optionally pinPINGRAM_FROM_SMS; otherwise Pingram uses your default number. - Add your phone to
PINGRAM_ALLOWED_USERS. - Start the gateway.
- 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
- Set
PINGRAM_API_KEY,PINGRAM_REGION, andPINGRAM_CHANNELS=email. Optionally setPINGRAM_FROM_EMAIL/PINGRAM_FROM_NAME; otherwise Pingram uses its default sender. - Add your email to
PINGRAM_ALLOWED_USERS. - Start the gateway.
- 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
frommust be inPINGRAM_ALLOWED_USERSunlessPINGRAM_ALLOW_ALL_USERS=true. With no allowlist andfalse, 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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hermes_pingram_gateway-1.0.0.tar.gz.
File metadata
- Download URL: hermes_pingram_gateway-1.0.0.tar.gz
- Upload date:
- Size: 19.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2536278c68cc81442bb07f4873bd94fe24ff963c10bbbf5e6fb74c7aaaa72722
|
|
| MD5 |
c1de521c9d743917e56a06384319c5bf
|
|
| BLAKE2b-256 |
a4e40f45c7825441a54ad17b4afce6d068aa9ec3337c55d90df84e3a665bef0f
|
Provenance
The following attestation bundles were made for hermes_pingram_gateway-1.0.0.tar.gz:
Publisher:
deploy.yml on pingram-io/hermes-gateway
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hermes_pingram_gateway-1.0.0.tar.gz -
Subject digest:
2536278c68cc81442bb07f4873bd94fe24ff963c10bbbf5e6fb74c7aaaa72722 - Sigstore transparency entry: 1700899428
- Sigstore integration time:
-
Permalink:
pingram-io/hermes-gateway@7a7a6cd655c4a55bacda1aa701caf7e5d728591a -
Branch / Tag:
refs/heads/main - Owner: https://github.com/pingram-io
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
deploy.yml@7a7a6cd655c4a55bacda1aa701caf7e5d728591a -
Trigger Event:
push
-
Statement type:
File details
Details for the file hermes_pingram_gateway-1.0.0-py3-none-any.whl.
File metadata
- Download URL: hermes_pingram_gateway-1.0.0-py3-none-any.whl
- Upload date:
- Size: 17.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bfba86f466c2f691cb01e0f8198a93810995b9b07189a87b99515586a8bd2baf
|
|
| MD5 |
7e7cf7395d4135f29842276d043aa043
|
|
| BLAKE2b-256 |
74285c768377d2b984eda9e063e5cc7d067a26fe4d5f6acb24f65b021593465d
|
Provenance
The following attestation bundles were made for hermes_pingram_gateway-1.0.0-py3-none-any.whl:
Publisher:
deploy.yml on pingram-io/hermes-gateway
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hermes_pingram_gateway-1.0.0-py3-none-any.whl -
Subject digest:
bfba86f466c2f691cb01e0f8198a93810995b9b07189a87b99515586a8bd2baf - Sigstore transparency entry: 1700899436
- Sigstore integration time:
-
Permalink:
pingram-io/hermes-gateway@7a7a6cd655c4a55bacda1aa701caf7e5d728591a -
Branch / Tag:
refs/heads/main - Owner: https://github.com/pingram-io
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
deploy.yml@7a7a6cd655c4a55bacda1aa701caf7e5d728591a -
Trigger Event:
push
-
Statement type: