# fde-sdk

Python SDK for the [FDE Fraud Decision Engine](https://fde.run-true.com) API.

Handles JWT+HMAC authentication automatically and provides typed methods
for all three V1 risk endpoints.

## Installation

```bash
pip install fde-sdk
# or from source:
pip install -e sdks/python/
```

## Requirements

- Python 3.9+
- `PyJWT >= 2.0`
- `httpx >= 0.24`

## Quick Start

```python
import asyncio
import os
from fde_sdk import FdeClient
from fde_sdk.types import EvaluateRequest, EventDetailModel, LoginDetail, CustKV

async def main():
    async with FdeClient(
        endpoint="https://fde.run-true.com",
        jwt_signing_key=os.environ["FDE_JWT_SIGNING_KEY"],
        hmac_key=os.environ["FDE_HMAC_KEY"],
        client_id=os.environ["FDE_CLIENT_ID"],
    ) as client:
        request = EvaluateRequest(
            eventCode="login",
            sessionId="sess-abc123",
            deviceToken=os.environ["DEVICE_TOKEN"],   # fingerprint from your SDK
            userId="user-42",
            eventDetail=EventDetailModel(
                Login=LoginDetail(
                    UserLoginName="alice@example.com",
                    Cust=[CustKV(key="browser", value="chrome")],
                )
            ),
        )
        response = await client.evaluate_risk(request)
        print(f"Decision: {response['decision']}  Score: {response['risk_score']}")

asyncio.run(main())
```

## Authentication

The SDK generates a fresh **HS256 JWT** (5-minute expiry) and computes an
**HMAC-SHA256 body signature** for every request. The HMAC algorithm is:

```
body_hash   = SHA256(body_bytes).hexdigest()
sign_string = timestamp + "\n" + body_hash
signature   = HMAC-SHA256(hmac_key_bytes, sign_string).hexdigest()
```

The body bytes used for signing are **identical** to the bytes sent over HTTP
— no intermediate re-serialisation.

## API Methods

### `evaluate_risk(request, explain=False) -> RiskEvaluateResponse`

POST `/v1/risk/evaluate` — real-time fraud score and decision.

Response keys: `request_id`, `risk_score` (int), `risk_level`, `decision`.
Optional: `banking_rule_details`, `explanation` (when `explain=True`),
`risk_labels`, `data_quality`, `list_actions`.

### `submit_outcome(request) -> OutcomeResponse`

POST `/v1/risk/outcome` — record the outcome of a transaction.

Response key: `request_id`.

### `submit_labels(request) -> SubmitLabelsResponse`

POST `/v1/risk/labels` — submit fraud ground-truth labels (up to 100 per call).

Response keys: `request_id`, `created`, `not_found`, `errors`, `results`.

## Error Handling

```python
from fde_sdk import FdeApiError

try:
    resp = await client.evaluate_risk(request)
except FdeApiError as e:
    print(f"HTTP {e.status_code}: {e.detail}")
```

## Event Types

| EventDetail field       | Event code examples                    |
|-------------------------|----------------------------------------|
| `Login`                 | `login`                                |
| `Register`              | `register`                             |
| `Transaction`           | `transaction`, `wire`, `ach`           |
| `LoginResult`           | `login_result`                         |
| `RegisterResult`        | `register_result`                      |
| `PasswordChange`        | `password_change`                      |
| `EmailChange`           | `email_change`                         |
| `SecurityVerification`  | `otp_verify`, `captcha`                |
| `PaymentResult`         | `payment_result`                       |
| `SmsResult`             | `sms_result`                           |

## Development

```bash
cd sdks/python
pip install -e ".[dev]"
pytest tests/ -v
```
