Skip to main content

A typed Python client for the Proofpoint Secure Email Relay administrative APIs built with Klarient.

Project description

Proofpoint Secure Email Relay API Package

Library implements the Proofpoint Secure Email Relay administrative APIs via Python.

Requirements:

  • Python 3.11+
  • klarient
  • requests
  • requests-oauth2client

Installing the Package

You can install the API library using the following command directly from Github.

pip install git+https://github.com/pfptcommunity/ser-admin-api-python.git

or can install the API library using pip.

pip install ser-admin-api

For local development from this folder, use an editable install.

python3 -m pip install -e .

Creating an API client object

SER credentials are issued as a principal and secret. SERClient handles the OAuth client-credentials exchange internally using Proofpoint's token endpoint, then applies the issued token to each SER service host.

from ser_admin_api import SERClient

if __name__ == '__main__':
    client = SERClient("<enter_your_principal_here>", "<enter_your_secret_here>")

The client can also be used as a context manager.

from ser_admin_api import SERClient

if __name__ == '__main__':
    with SERClient("<enter_your_principal_here>", "<enter_your_secret_here>") as client:
        print(client.relay.relay_users.path)

Endpoint Examples

Endpoint-focused examples are available under examples/.

examples/
  connector_discovery.py
  connectors.py
  list_management_requests.py
  list_management_unsubscribe.py
  relay_config_discovery.py
  relay_users.py
  reporting_failures.py
  reporting_scoped_resources.py
  reporting_usage.py
  tag_management.py

Copy examples/settings.example.json to examples/settings.json and add your principal and secret to run them locally.

The examples are intentionally resource-focused so each file demonstrates one small part of the SER API tree. They are read-only by default. Mutation examples are shown as request shapes or commented calls because connector and relay-user delete operations are not exposed by the documented API.

Resource Paths

The API is modeled as a resource tree. Each resource exposes its path and URL, which can be useful when learning or debugging the wrapper.

from ser_admin_api import SERClient

if __name__ == '__main__':
    client = SERClient("<enter_your_principal_here>", "<enter_your_secret_here>")

    print(client.relay.relay_users.path)
    # /v1/relay-users

    print(client.connector_config.connectors.details.path)
    # /v1/connectors/details

    print(client.list_management.lists.unsubscribe.requests.url)
    # https://list-management.ser.proofpoint.com/v1/lists/unsubscribe/requests

Modeled API Roots

The SER documentation exposes separate hosts for the API areas. SERClient is a facade that owns one Klarient client per service host while sharing one OAuth-backed session.

  • Reporting: https://reporting.ser.proofpoint.com
  • Reporting v2: https://reporting.ser.proofpoint.com
  • Relay User Management: https://relay-config.ser.proofpoint.com
  • Connector Management: https://connector-config.ser.proofpoint.com
  • Tag Management: https://tag-management.ser.proofpoint.com
  • List Management: https://list-management.ser.proofpoint.com

You can override any base URL in the constructor for tests or private deployments.

Querying Relay Users

from ser_admin_api import SERClient
from ser_admin_api.relay_users import RelayUsersQuery

if __name__ == '__main__':
    client = SERClient("<enter_your_principal_here>", "<enter_your_secret_here>")

    relay_users = client.relay.relay_users.retrieve(RelayUsersQuery(page=1, size=100))

    print("Status: {}".format(relay_users.status))
    print("Reason: {}".format(relay_users.reason))
    print("Total Records: {}".format(relay_users.record_count))

    for relay_user in relay_users:
        print(relay_user.relay_user_id)
        print(relay_user.name)
        print(relay_user.status)

Querying Connectors

from ser_admin_api import SERClient
from ser_admin_api.connectors import ConnectorInfoQuery

if __name__ == '__main__':
    client = SERClient("<enter_your_principal_here>", "<enter_your_secret_here>")

    connectors = client.connector_config.connectors.retrieve(ConnectorInfoQuery(page=1, size=100))

    for connector in connectors:
        print(connector.connector_id)
        print(connector.name)
        print(connector.status)

Querying Tags

from ser_admin_api import SERClient
from ser_admin_api.tags import TagInfoQuery

if __name__ == '__main__':
    client = SERClient("<enter_your_principal_here>", "<enter_your_secret_here>")

    tags = client.tag_management.tags.retrieve(TagInfoQuery(page=1, size=100))

    for tag in tags:
        print(tag.tag_id)
        print(tag.name)

Querying Unsubscribe Lists

from ser_admin_api import SERClient
from ser_admin_api.suppression import UnsubscribeListQuery

if __name__ == '__main__':
    client = SERClient("<enter_your_principal_here>", "<enter_your_secret_here>")

    lists = client.list_management.lists.unsubscribe.retrieve(
        UnsubscribeListQuery(page=1, size=100)
    )

    for unsubscribe_list in lists:
        print(unsubscribe_list.list_id)
        print(unsubscribe_list.name)

Querying Reporting Data

from datetime import date, timedelta

from ser_admin_api import SERClient
from ser_admin_api.reporting.failures import ReportInterval
from ser_admin_api.reporting.usage import UsageMetricsRequest, UsageTrafficSummaryQuery, UsageTrendRequest

if __name__ == '__main__':
    client = SERClient("<enter_your_principal_here>", "<enter_your_secret_here>")

    end = date.today()
    start = end - timedelta(days=7)

    traffic = client.reporting.usage.traffic_summary.retrieve(
        UsageTrafficSummaryQuery().with_dates(gte=start, lte=end)
    )
    print(traffic.data.accepted_messages)

    relay_usage = client.reporting.usage.relay_users.retrieve(
        UsageMetricsRequest().with_dates(gte=start, lte=end).with_page(1, 100)
    )
    for row in relay_usage:
        print(row.relay_user_id)
        print(row.total_messages)

    trend = client.reporting.usage.message_trend.retrieve(
        UsageTrendRequest().with_dates(gte=start, lte=end).with_interval(ReportInterval.DAY)
    )
    print(trend.data)

Pagination

List-style resources are pageable. Calling retrieve() returns a Page object. Iterating a pageable resource yields page objects. Calling items() flattens page boundaries and yields typed rows.

from ser_admin_api import SERClient

if __name__ == '__main__':
    client = SERClient("<enter_your_principal_here>", "<enter_your_secret_here>")

    for page in client.relay.relay_users:
        print("Current Page Number: {}".format(page.current_page_number))
        print("Page Size: {}".format(page.page_size))
        print("Total Records: {}".format(page.record_count))
        for relay_user in page:
            print(relay_user.name)
        break

    for relay_user in client.relay.relay_users.items():
        print(relay_user.name)

Common pageable resources include:

client.relay.verified_domains
client.relay.relay_users
client.relay.relay_users.search
client.connector_config.connectors
client.connector_config.connectors.search
client.connector_config.connectors.details
client.connector_config.connectors.details.search
client.tag_management.tags
client.tag_management.tags["tag-id"].notes
client.list_management.lists.unsubscribe
client.list_management.lists.unsubscribe["list-id"].addresses
client.list_management.lists.unsubscribe["list-id"].relay_users
client.list_management.lists.unsubscribe.requests
client.reporting.usage.relay_users
client.reporting.usage.tags
client.reporting.failures.relay_users
client.reporting.failures.tags

Creating Or Updating Resources

Mutation request bodies are typed request objects. Constructor arguments model required or common fields. Builder methods add optional fields while keeping the final JSON body aligned with the SER API.

from ser_admin_api.relay_users import RelayUserCreate

create_request = (
    RelayUserCreate(
        username="example",
        display_name="Example Relay User",
        domain="example.com",
    )
    .with_tag("tag-id")
)

print(create_request.to_mapping())
from ser_admin_api.connectors import ConnectorCreate

create_request = (
    ConnectorCreate(name="Example Connector", port=587, region="US")
    .with_allowed_ip("192.0.2.10")
)

print(create_request.to_mapping())

Connector and relay-user create/update/status calls modify tenant configuration. The examples show the request shapes, and tools/live_audit.py contains opt-in live validation flows for disposable records.

Typed Response Objects

Responses expose quick access to status, reason, URL, the decoded payload, and the native requests response through the underlying Klarient response object. SER rows are modeled as typed dictionary wrappers: known fields have Pythonic properties, and newly-added API fields remain available through dictionary-style access.

connector = client.connector_config.connectors["connector-id"].retrieve()

print(connector.status)
print(connector.data.connector_id)
print(connector.data.name)

# New or endpoint-specific fields remain available from the underlying mapping.
print(connector.data.get("newFieldFromApi"))

Network Options

Network settings such as proxy, timeout, and SSL verification are configured with RequestsOptions.

from klarient import RequestsOptions, RequestsTimeout
from ser_admin_api import SERClient

if __name__ == '__main__':
    client = SERClient(
        "<enter_your_principal_here>",
        "<enter_your_secret_here>",
        options=RequestsOptions(
            timeout=RequestsTimeout(connect=10, read=600),
            proxy="http://proxy.example.com:3128",
            verify_ssl=True,
        ),
    )

Proxy Support

A single proxy URL is used for both HTTP and HTTPS requests.

SOCKS5 proxy example:

from klarient import RequestsOptions
from ser_admin_api import SERClient

if __name__ == '__main__':
    client = SERClient(
        "<enter_your_principal_here>",
        "<enter_your_secret_here>",
        options=RequestsOptions(
            proxy="socks5h://proxyuser:proxypass@proxy.example.com:8128",
        ),
    )

HTTP proxy example:

from klarient import RequestsOptions
from ser_admin_api import SERClient

if __name__ == '__main__':
    client = SERClient(
        "<enter_your_principal_here>",
        "<enter_your_secret_here>",
        options=RequestsOptions(
            proxy="http://proxyuser:proxypass@proxy.example.com:8080",
        ),
    )

If your environment requires different proxies by scheme, pass a requests-style mapping:

from klarient import RequestsOptions
from ser_admin_api import SERClient

if __name__ == '__main__':
    client = SERClient(
        "<enter_your_principal_here>",
        "<enter_your_secret_here>",
        options=RequestsOptions(
            proxy={
                "http": "http://proxy.example.com:8080",
                "https": "http://secure-proxy.example.com:8080",
            },
        ),
    )

If your environment already uses standard proxy variables, those can also be used.

export HTTP_PROXY="http://proxy.example.com:8080"
export HTTPS_PROXY="http://proxy.example.com:8080"

HTTP Timeout Settings

from klarient import RequestsOptions, RequestsTimeout
from ser_admin_api import SERClient

if __name__ == '__main__':
    client = SERClient(
        "<enter_your_principal_here>",
        "<enter_your_secret_here>",
        options=RequestsOptions(timeout=RequestsTimeout(connect=10, read=600)),
    )

Endpoint Coverage

The wrapper models the reviewed SER API areas:

  • Reporting usage v1 and v2 overview
  • Reporting failures
  • Relay configuration and relay users
  • Connector configuration
  • Tag management
  • List management unsubscribe and unsubscribe requests

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ser_admin_api-0.1.0.tar.gz (62.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ser_admin_api-0.1.0-py3-none-any.whl (78.5 kB view details)

Uploaded Python 3

File details

Details for the file ser_admin_api-0.1.0.tar.gz.

File metadata

  • Download URL: ser_admin_api-0.1.0.tar.gz
  • Upload date:
  • Size: 62.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ser_admin_api-0.1.0.tar.gz
Algorithm Hash digest
SHA256 fd96d0f94f38bfee08e632b7f5ce7f20a0f1751a588baf91b3d7bddb70a25ae2
MD5 cac41171a0dea22ef7cd736dbeaec093
BLAKE2b-256 717bed77ab42499c52449b98f1f230ffc7da75f53e38e99d9731e9c85fba82e4

See more details on using hashes here.

Provenance

The following attestation bundles were made for ser_admin_api-0.1.0.tar.gz:

Publisher: python-publish.yml on pfptcommunity/ser-admin-api-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ser_admin_api-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: ser_admin_api-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 78.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ser_admin_api-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 54881b1ea660a430e6ebefeb679b135c9a3425ecaf377db72ee1a50619993f22
MD5 22009d0c48b960bc12287b20aefe1988
BLAKE2b-256 7ed56e373b091a7100d8f1dee3dd96c75a09ebeba38863ee6dc83033c1ea69db

See more details on using hashes here.

Provenance

The following attestation bundles were made for ser_admin_api-0.1.0-py3-none-any.whl:

Publisher: python-publish.yml on pfptcommunity/ser-admin-api-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page