Live Mailprotector CloudFilter REST client (emailservice.io/api/v1,
Bearer auth via vault msp-tools/mailprotector.sops.yaml). Lists mail-flow
logs and held/quarantined messages across client domains and releases them
(POST messages/{id}/deliver, deliver_many). Read-only by default; every
release/rule-add/config-change gated behind --confirm. Mirrors the
packetdial skill pattern. Built after diagnosing a Dataforth held-outbound
message that never reached ACG.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6.7 KiB
name, description
| name | description |
|---|---|
| mailprotector | Manage the Arizona Computer Guru (ACG) Mailprotector CloudFilter email-security gateway via the live CloudFilter REST API (emailservice.io). Search and release held / quarantined mail (inbound and outbound), pull mail-flow logs to explain why a message did or did not deliver, inspect entity configuration and allow/block rules, find a user or alias by email, and manage allow/block rules. Read-only by default; every release / rule-add / config-change is gated behind --confirm. Invoke for: "mailprotector", "cloudfilter", "emailservice.io", "held mail", "quarantined email", "release email", "outbound quarantine", "why didn't my email arrive", "email security gateway", "INKY", "mail flow logs", "allow block rule", "release spam". This skill talks to the LIVE production reseller CloudFilter platform — treat releases conservatively. |
Mailprotector / CloudFilter Skill
Standalone CLI client for the Mailprotector CloudFilter REST API
(emailservice.io), the reseller email-security platform ACG layers on top of
client mail flow. Read-only by default; every write (release, rule add, config
change) is gated behind --confirm.
The two-layer context (important)
ACG's email security sits in front of client mailboxes as two cooperating layers:
| Layer | What it does |
|---|---|
| Mailprotector CloudFilter | The delivery / filtering gateway. Inbound and outbound mail passes through it; spam, virus, and policy hits are held / quarantined here. Releasing a held message re-injects it for delivery. This is the API this skill drives. |
| INKY | Email annotation / phishing-banner layer. Adds the warning banners and protects against impersonation. Not part of this API surface. |
Both sit layered on top of the client's own Exchange / M365 mail flow — so a
"missing email" investigation usually means: was it held at CloudFilter (check
messages / logs), or did it pass CloudFilter and stall in Exchange?
Connection
| Item | Value |
|---|---|
| Base URL | https://emailservice.io/api/v1 (override MAILPROTECTOR_API_BASE_URL) |
| Auth | Authorization: Bearer <api_key> |
| Vault entry | msp-tools/mailprotector.sops.yaml, field credentials.api_key |
| Env override | MAILPROTECTOR_API_KEY |
Credential resolution order: MAILPROTECTOR_API_KEY env -> vault
credentials.api_key. The key is never hardcoded; a clear setup error is raised
if neither resolves.
Scopes
Five entity types carry logs / messages / configuration /
allow_block_rules / users / domains sub-resources. Path form is
/{scope}/{id}/...:
resellers, customers, domains, user_groups, users
The CLI validates scope against this set.
Running the CLI
This machine's Python launcher is py (per identity.json); python / python3
also work. Run from the scripts dir so the two modules resolve.
cd C:/claudetools/.claude/skills/mailprotector/scripts
py mp.py status # validate token (GET /domains, per_page=1)
py mp.py domains # list domains (global)
py mp.py domains --scope customers --id <id>
py mp.py domain <domain_id>
py mp.py customers <reseller_id>
py mp.py customer <customer_id>
py mp.py users <scope> <id>
py mp.py user <user_id>
py mp.py find-user user@client.com # locate a user / alias by email (a READ)
py mp.py config <scope> <id> # shows permissions.messages.allow_spam_release
py mp.py rules <scope> <id>
Mail-flow logs and held mail (the common investigation)
Both accept the same filters: --sender --recipient --subject --decision --sort-field --sort-direction --page --page-size.
# Why didn't this arrive? Look at the decision in the flow logs.
py mp.py logs domains <domain_id> --recipient ceo@client.com --decision quarantine_spam
# Held / quarantined mail search.
py mp.py messages domains <domain_id> --sender boss@vendor.com
--decision values: default, deliver, quarantine_spam,
quarantine_virus, quarantine_policy, bounce, encrypt, delete.
--sort-field values: @timestamp (default), prime.direction,
prime.from_header_raw, prime.recipient, prime.subject, prime.decision,
prime.score.
Writes (gated)
Every mutating command prints a [DRY RUN] line and exits non-zero unless you
pass --confirm.
py mp.py release <message_id> --confirm
py mp.py release <message_id> --recipients alt@client.com --confirm
py mp.py release-many <scope> <id> --ids 111,222,333 --confirm
py mp.py release-many <scope> <id> --all --confirm
py mp.py add-rule <scope> <id> --value vendor.com --type allow --confirm
py mp.py enable-release <scope> <id> --confirm
The allow_spam_release gotcha
Releasing a held spam message fails if the owning entity does not have
permissions.messages.allow_spam_release = true. Workflow:
py mp.py config <scope> <id>— checkallow_spam_release.- If
false:py mp.py enable-release <scope> <id> --confirm. - Re-run the
release/release-many.
Virus and policy quarantines are governed separately — only spam release is gated by this permission.
Example workflow: find a client's held outbound mail from a sender and release it
# 1. Find the client's domain.
py mp.py domains --scope customers --id <customer_id>
# 2. Search held messages from the sender (outbound = sender is the client user).
py mp.py messages domains <domain_id> --sender user@client.com --decision quarantine_spam
# 3. If it's spam-held, make sure release is permitted on the domain.
py mp.py config domains <domain_id> # check allow_spam_release
py mp.py enable-release domains <domain_id> --confirm # only if needed
# 4. Release by message id (DRY RUN first — omit --confirm to preview).
py mp.py release <message_id> # [DRY RUN]
py mp.py release <message_id> --confirm # actually release
Raw escape hatch
The named commands cover the common surface; for anything else, hit the path
directly. Non-GET methods still require --confirm.
py mp.py raw GET domains/<id>/logs
py mp.py raw POST messages/<id>/deliver --body '{"include_original_recipients":1}' --confirm
Notes
- This is the LIVE production reseller CloudFilter platform. A release
re-delivers real mail to real recipients, and an allow rule can let real spam
or phishing through — confirm the target entity with a read command before any
write, and prefer releasing specific message ids over
--all. - Pagination:
page(default 1) andper_page(default 25); resellermessagescapsper_pageat 50. TheX-Paginationresponse header carries the page/total metadata. - Full endpoint catalog, filter tables, and the global
field[op]=valueoperators live inreferences/api.md.