# Official Gateman Python client

The official Gateman Python client is a synchronous implementation of the
authenticated transport and authentication boundary defined by [Contract v1](https://docs.getgateman.com/contract-v1/).
PHP, Node.js, Python, and Go are equally official Gateman clients.

Client version: 1.0.0.

## Requirements and acquisition

The client supports Python 3.11 or newer. Runtime dependencies are:

```python
httpx>=0.27,<1
cryptography>=41,<52
```

Gateman distributes a source package archive from:

```text
GET https://getgateman.com/client/python
```

The archive contains src/getgateman, pyproject.toml, README, and py.typed. It
is not evidence of PyPI publication. Install it with normal Python tooling:

```bash
mkdir -p vendor
curl --fail --silent --show-error https://getgateman.com/client/python -o /tmp/getgateman.tar.gz
tar -xzf /tmp/getgateman.tar.gz --strip-components=1 -C vendor
python -m pip install ./vendor
```

The installation resolves the declared runtime dependencies. The client is
synchronous and has no asynchronous API.

## Configuration and initialization

GatemanConfig is a frozen dataclass:

```python
base_url: str
app_id: str  # Gateman-generated permanent technical application identity
private_key: str | None = None
app_secret: str | None = None
environment: str | None = None
```

Configuration is explicit. The package does not read environment variables or
load .env files. base_url must use http or https, include a host, and contain
no query or fragment. app_id is required. It is the generated permanent technical identity; the customer-facing application name is managed separately in the Gateman console.

private_key is canonical standard Base64 containing a raw 32-byte Ed25519
seed. It takes precedence over app_secret. Invalid key material raises a
configuration error without falling back to HMAC. environment, when supplied,
must be production, staging, or development; no default is selected.

```python
from getgateman import GatemanClient, GatemanConfig
```

```python
config = GatemanConfig(
    base_url='https://getgateman.com',
    app_id='generated-app-id-from-gateman',
    app_secret='application-secret-from-gateman',
)
```

```python
with GatemanClient(config) as client:
    response = client.request('/client/get', {})
    if 200 <= response.status < 300:
        print(response.json)
```

POST /client/get requires client:read. The values shown are placeholders, not
credentials.

## Public API

GatemanClient(config)

Constructs a reusable synchronous client. Invalid configuration raises
GatemanClientError with kind configuration.

client.request(path, payload=_UNSET, *, idempotency_key=None,
credential_request_id=None, timeout=None)

The omitted payload becomes {}. Explicit None, lists, and other JSON values
are accepted on ordinary routes. Environment-aware routes require a mapping.
JSON is compact UTF-8; NaN and infinity are rejected locally.

idempotency_key is trimmed around spaces and tabs, omitted when empty, limited
to 255 UTF-8 bytes, and signed. It does not itself provide route-level
idempotency. credential_request_id is valid only for /secrets/get and
/db/get; those routes generate a fresh lowercase 32-hex-character ID when it
is omitted.

timeout overrides the default ten-second request timeout and must be finite
and positive. The HTTPX connect timeout is capped at one second.

Paths must be absolute and may not contain queries, fragments, controls,
unencoded spaces, Unicode, dot segments, or transport-normalizing forms.
Percent-encoded paths are accepted when transport-equivalent.

client.health()

Performs unauthenticated GET /health with a three-second timeout. A healthy
response does not prove application authentication or provider readiness.

client.close()

Closes the underlying HTTPX client. GatemanClient also supports with statements
and closes automatically at the end of the block.

## Environment handling

The client receives the application environment from `GatemanConfig.environment`.
It accepts `production`, `staging`, and `development`, and trims and lowercases
the configured value. Requests to routes that require an environment raise a
local configuration error when it is absent or invalid; the normalized value
is injected automatically. See [Contract v1](https://docs.getgateman.com/contract-v1/)
for the authoritative route classification.

If a payload already contains `environment`, its normalized value must match
the configured value; a conflict raises `GatemanClientError` with kind
`validation`. The top-level mapping is copied before injection, so the
caller’s mapping is not changed. Environment-independent routes receive no
automatic environment, and no implicit default is selected.

## Responses and errors

GatemanResponse is a frozen dataclass:

```python
status: int
headers: Mapping[str, str]
raw_body: bytes
json: T | None
request_id: str | None
retry_after: int | None
decode_error: GatemanClientError | None
```

Every received HTTP response, including non-2xx statuses, is returned.
raw_body preserves the response as bytes. Valid JSON is available through
json. Malformed JSON leaves raw_body intact and sets decode_error with kind
decode. Empty bodies do not create a decode error.

request_id is populated only for a 16-character lowercase hexadecimal
X-Gateman-Request-ID. retry_after is populated for a decimal integer
Retry-After value. Local configuration, validation, serialization, transport,
timeout, and redirect failures raise GatemanClientError with kind
configuration, validation, serialization, transport, timeout, or decode.

Redirect responses are rejected and requests are never retried automatically.

The shared wire-level signing and authentication rules are defined by [Contract v1](https://docs.getgateman.com/contract-v1/).
