Files
claudetools/.claude/skills/mailprotector/references/api.md
Mike Swanson 9b1c5c391d harness: fix py-vs-python3 doc gap — add py.sh resolver, repoint skill/command docs
The skill/command DOCS instructed Claude to run a bare `py ...`, which is the
Windows py-launcher — absent on Linux/macOS (exit 127, hit on GURU-KALI). A blind
py->python3 swap is wrong too: python3 is a broken MS Store shim on some Windows
boxes where `py` is the correct launcher.

Fix mirrors the resolution the .sh skill scripts already do:
- New .claude/scripts/py.sh: picks the interpreter that actually RUNS —
  identity.json python.command first, then py -> python3 -> python, each
  validated with `-c 'import sys'` so the MS Store stub is skipped. exec's it.
- Repointed all DOC invocations (10 files, ~70 sites) from `py ...` to
  `bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ...` (incl. the `py -c` and
  `py -` heredoc forms in checkpoint.md / mailbox.md).
- Left the .sh skill scripts untouched — they already resolve py/python/python3.
- errorlog.md: marked the GURU-KALI entry RESOLVED.

Depends on CLAUDETOOLS_ROOT (seeded by ensure-settings-env.py); py.sh also
self-resolves the repo root via git/cwd as a fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 09:48:54 -07:00

5.5 KiB

Mailprotector CloudFilter REST API — Reference

Full endpoint catalog and filter tables for the mailprotector skill. SKILL.md stays lean; the detail lives here.

Connection

Item Value
Base URL https://emailservice.io/api/v1
Override env MAILPROTECTOR_API_BASE_URL
Auth Authorization: Bearer <api_key>
Key env override MAILPROTECTOR_API_KEY
Vault entry msp-tools/mailprotector.sops.yaml, field credentials.api_key

Credential resolution order: MAILPROTECTOR_API_KEY env -> vault credentials.api_key (read via bash <root>/.claude/scripts/vault.sh get-field). A clear setup error is raised if neither resolves.

Scopes

The five entity types that carry logs, messages, configuration, users, domains, and allow_block_rules sub-resources. Path form is /{scope}/{id}/...:

resellers, customers, domains, user_groups, users

The CLI validates scope against this set.

Pagination

Param Default Notes
page 1 1-indexed page number
per_page 25 Max 50 on reseller messages

The response includes an X-Pagination response header (a JSON document with the page/total metadata).

Global list filtering

List endpoints accept field[op]=value filters. Operators:

Op Meaning
Gt greater than
Geq greater than or equal
Lt less than
Leq less than or equal
Eq equal

Example: created_at[geq]=2026-06-01.

Logs / messages filtering

Every .../logs and .../messages endpoint accepts these params:

Param Default Allowed values
sort_direction desc desc, asc
sort_field @timestamp @timestamp, prime.direction, prime.from_header_raw, prime.recipient, prime.subject, prime.decision, prime.score
page 1 integer
page_size (API default) integer
sender (none) sender filter
recipient (none) recipient filter
subject (none) subject filter
decision all default, deliver, quarantine_spam, quarantine_virus, quarantine_policy, bounce, encrypt, delete

READ endpoints

Method Path Client method CLI
GET /domains domains() domains
GET /{scope}/{id}/domains domains(scope,id) domains --scope --id
GET /domains/{id} domain(id) domain <id>
GET /resellers/{id}/customers customers(id) customers <reseller_id>
GET /customers/{id} customer(id) customer <id>
GET /{scope}/{id}/users users(scope,id) users <scope> <id>
GET /users/{id} user(id) user <id>
POST /users/find_by_address find_user(address) find-user <address>
GET /{scope}/{id}/logs logs(scope,id,...) logs <scope> <id>
GET /{scope}/{id}/messages messages(scope,id,...) messages <scope> <id>
GET /{scope}/{id}/configuration configuration(scope,id) config <scope> <id>
GET /{scope}/{id}/allow_block_rules allow_block_rules(scope,id) rules <scope> <id>

find_by_address is a READ despite being a POST — it looks up a user / alias by email. It is NOT gated behind --confirm.

status is a synthetic read: GET /domains?per_page=1 used purely to validate the bearer token (HTTP 200 = key good).

WRITE endpoints (gated behind --confirm)

Without --confirm the CLI prints [DRY RUN] Would <action>: <detail> and exits with code 2. With --confirm it performs the call.

Release one held message

POST /messages/{message_id}/deliver
body: {"include_original_recipients": 1, "recipients": "<optional csv>"}

Client: release_message(message_id, recipients=None) — CLI: release <message_id> [--recipients csv] --confirm

Bulk release held messages

POST /{scope}/{id}/messages/deliver_many
body: {"include_original_recipients": 1, "recipients": "<optional>",
       "all_selected": false, "ids": "<csv ids>"}

Client: release_many(scope, id, ids=None, all_selected=False, recipients=None) CLI: release-many <scope> <id> [--ids csv | --all] [--recipients csv] --confirm

Add allow / block rule

POST /{scope}/{id}/allow_block_rules
body: {"value": "...", "rule_type": "allow" | "block"}

Client: add_rule(scope, id, value, rule_type) — CLI: add-rule <scope> <id> --value <v> --type allow|block --confirm

Enable spam release on an entity

PUT /{scope}/{id}/configuration
body: {"permissions": {"messages": {"allow_spam_release": true}}}

Client: enable_release(scope, id) — CLI: enable-release <scope> <id> --confirm

This is required before an entity's held spam can be released. Check the state first with config <scope> <id> and look at permissions.messages.allow_spam_release.

Raw escape hatch

bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py raw <METHOD> <path> [--body JSON] [--confirm]

Non-GET methods require --confirm. Use for any endpoint not wrapped by a named command.

The allow_spam_release gotcha

Releasing a held spam message will fail (or silently no-op) if the owning entity does not have permissions.messages.allow_spam_release = true. The fix:

  1. bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py config <scope> <id> — confirm allow_spam_release is false.
  2. bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py enable-release <scope> <id> --confirm — flip it to true.
  3. Re-run the release / release-many.

Virus and policy quarantines are governed separately — only spam release is gated by this permission.