apploitech-apploi-client 0.1.2
Official Python client for Apploi Partner API with high-level abstractions
Requires Python: >=3.8
Keywords: apploi, api, client, sdk, partner-api, hr, recruitment, applicant-tracking

# Python Adapter for Apploi Partner API

A clean, type-safe Python adapter that wraps the Fern-generated SDK with better abstractions, validation, and error handling.

## Installation

```bash
# Install dependencies
pip install httpx python-dateutil

# Or install all requirements
pip install -r requirements.txt
```

## Quick Start

```python
from adapters.python import ApploiClient
from datetime import date

# Initialize client
client = ApploiClient(
    api_key="your-api-key",
    authorization="your-auth-token"
)

# Get job applications with native Python types
applicants = client.get_job_applications(
    job_id=12345,                    # Pass int, not string!
    updated_after=date(2024, 1, 1),  # Pass date object!
    limit=50                         # Pass int, not string!
)

# Work with strongly-typed results
for applicant in applicants:
    print(f"{applicant.name} - {applicant.status.value}")
    print(f"  Email: {applicant.email}")
    print(f"  Applied: {applicant.applied_at}")
```

> **💡 See `example_usage.py` for comprehensive usage examples including pagination, error handling, and comparisons with the raw SDK.**

## Features

- ✅ **Native Python Types**: Use `int`, `date`, `datetime` instead of strings
- ✅ **Type Safety**: Dataclasses with proper type hints
- ✅ **Validation**: Parameters validated before API calls
- ✅ **Error Handling**: Custom exception hierarchy
- ✅ **IDE Support**: Full autocomplete and type checking

## API Reference

### Client Initialization

```python
from adapters.python import ApploiClient

client = ApploiClient(
    api_key="your-api-key",        # Required
    authorization="your-auth",      # Required
    base_url=None,                  # Optional, defaults to production
    timeout=60                      # Optional, request timeout in seconds
)
```

### get_job_applications()

Retrieve job applications with filtering and pagination.

```python
applicants = client.get_job_applications(
    state="applied",                          # Filter by status
    query="engineer",                         # Search query
    limit=50,                                 # Max results (1-1000)
    team_id=123,                             # Team ID (int or str)
    updated_before=datetime.now(),           # Before date
    offset=0,                                # Pagination offset
    job_id=456,                              # Job ID (int or str)
    updated_after=date(2024, 1, 1)          # After date
)
```

**Parameters:**
- All parameters are optional
- IDs accept `int` or `str`
- Dates accept `date`, `datetime`, or ISO string
- Limit must be 1-1000
- Offset must be ≥ 0

**Returns:** `ApplicantsList` with:
- `items`: List of `JobApplication` objects
- `total`: Total count available
- `limit`: Page size
- `offset`: Current offset
- `has_more`: Boolean for more pages

## Domain Models

### JobApplication

```python
@dataclass
class JobApplication:
    applicant_id: str
    job_id: str
    status: ApplicationStatus
    first_name: Optional[str]
    last_name: Optional[str]
    email: Optional[str]
    phone: Optional[str]
    applied_at: Optional[datetime]
    updated_at: Optional[datetime]
    team_id: Optional[str]

    @property
    def name(self) -> str:
        """Full name (first + last)"""
```

### ApplicationStatus

```python
class ApplicationStatus(Enum):
    APPLIED = "applied"
    SCREENING = "screening"
    INTERVIEWING = "interviewing"
    REFERENCE_CHECK = "reference_check"
    OFFER_EXTENDED = "offer_extended"
    HIRED = "hired"
    REJECTED = "rejected"
    WITHDRAWN = "withdrawn"
    ON_HOLD = "on_hold"
    UNKNOWN = "unknown"  # Fallback for unrecognized values
```

**Note:** The enum supports legacy values `"interview"` and `"offer"` for backwards compatibility, automatically mapping them to `INTERVIEWING` and `OFFER_EXTENDED` respectively.

## Error Handling

```python
from adapters.python import (
    ApploiValidationError,
    ApploiAuthenticationError,
    ApploiAPIError
)

try:
    applicants = client.get_job_applications(limit=50)

except ApploiValidationError as e:
    # Invalid parameters (e.g., limit > 1000)
    print(f"Validation error: {e}")

except ApploiAuthenticationError as e:
    # 401/403 errors
    print(f"Auth failed: {e}")

except ApploiAPIError as e:
    # Other API errors
    print(f"API error: {e}")
    if e.status_code == 429:
        # Handle rate limiting
        time.sleep(60)
```

## Usage Examples

### Basic Usage

```python
# Get all applicants
all_applicants = client.get_job_applications()
print(f"Found {len(all_applicants)} applicants")
```

### Filtering

```python
# Filter with native types
filtered = client.get_job_applications(
    job_id=12345,                         # Integer
    team_id=789,                          # Integer
    updated_after=date(2024, 1, 1),       # Date object
    state="applied",                      # String
    limit=50                              # Integer
)
```

### Pagination

```python
# Paginate through all results
all_results = []
offset = 0
page_size = 50

while True:
    page = client.get_job_applications(
        limit=page_size,
        offset=offset
    )

    all_results.extend(page.items)

    if not page.has_more:
        break

    offset += page_size

print(f"Total fetched: {len(all_results)}")
```

### Working with Results

```python
result = client.get_job_applications(limit=1)

if result:
    applicant = result[0]

    # Strongly typed fields
    print(f"ID: {applicant.applicant_id}")
    print(f"Name: {applicant.name}")
    print(f"Status: {applicant.status.value}")

    # Date handling
    if applicant.applied_at:
        days_ago = (datetime.now() - applicant.applied_at).days
        print(f"Applied {days_ago} days ago")

    # Access raw data if needed
    raw = applicant.raw_data
```

## Testing

```bash
# Run all tests
pytest tests/

# Run specific test file
pytest tests/test_client.py -v

# Run with coverage
pytest tests/ --cov=adapters.python

# Integration tests (requires credentials)
export APPLOI_API_KEY="your-key"
export APPLOI_AUTHORIZATION="your-auth"
pytest tests/integration/ -m integration
```

## Project Structure

```
python/
├── __init__.py              # Package initialization
├── client.py                # Main ApploiClient class
├── exceptions.py            # Custom exceptions
├── models/
│   ├── __init__.py
│   ├── job_application.py  # JobApplication, JobApplicationsList
│   └── common.py           # Shared models
├── validators/
│   ├── __init__.py
│   ├── dates.py            # Date validation/parsing
│   └── parameters.py       # Parameter validation
├── example_usage.py        # Usage examples
├── requirements.txt        # Dependencies
└── README.md              # This file
```

## Requirements

- Python 3.7+
- httpx (for Fern SDK)
- python-dateutil (optional, for date parsing)

## Differences from Raw SDK

### Without Adapter (Fern SDK)
```python
# Everything must be strings
sdk.applicants.get_applicants(
    job_id="12345",
    limit="50",
    updated_after="2024-01-01"
)  # Returns: Dict[str, Any]
```

### With Adapter
```python
# Native Python types
client.get_job_applications(
    job_id=12345,
    limit=50,
    updated_after=date(2024, 1, 1)
)  # Returns: ApplicantsList (typed)
```

## Architecture

See [ARCHITECTURE.md](./ARCHITECTURE.md) for detailed design documentation.

## Contributing

1. Follow existing patterns
2. Add tests for new features
3. Update type hints
4. Run tests before submitting

## License

[Your License Here]