# HRIS Private API

This site contains the technical resources required to build internal integrations or automation using CATAPA's private endpoints.

{% hint style="danger" %}
This is not intended for public use
{% endhint %}

This section is designed for developers who need to interact with CATAPA features that is not exposed via the Public API. If you are an external developer looking to build a standard integration using the Public API, please refer to the [Public API Section](https://app.gitbook.com/o/l7iZNPfPGNFLFlscNeoe/s/XbRDlxflF58D1QG2tLmP/~/edit/~/changes/214/developer-documentation/hris-public-api). **Ignorance of this notice might lead to break in your integration** as our private API contract might change without public notice.

### Installation

{% tabs %}
{% tab title="pip" %}

```bash
pip install catapa-private
```

{% endtab %}

{% tab title="poetry" %}

```bash
poetry add catapa-private
```

{% endtab %}

{% tab title="uv" %}

```bash
uv add catapa-private
```

{% endtab %}
{% endtabs %}

### Quick Start

A complete Hello World example to get you started immediately.

```python
from catapa_private import CatapaPrivate

def main() -> None:
    client = CatapaPrivate(
        tenant="zfrl",
        username="demo",
        password="dmo-password"
    )

    response = client.get("/core/countries", params={"page": 0, "size": 10})
    response.raise_for_status()
    data = response.json()
    print(f"Found {len(data.get('content', []))} countries")

if __name__ == "__main__":
    main()
```

> **💡 Tip:** By default, the SDK connects to `https://api.catapa.com`. To use a different base URL (e.g., for staging or testing), pass the `base_url` parameter:
>
> ```python
> client = CatapaPrivate(
>     ...,
>     base_url="https://api-development.catapa.com"
> )
> ```

### Getting Your Credentials

To use the SDK with your own account, you'll need the following authentication credentials:

#### Tenant ID

Your **tenant ID** is your organization's unique identifier in CATAPA. To obtain it, please contact <support@catapa.com>.

#### Username and Password

Your **username** and **password** are your CATAPA account credentials used for session-based authentication.

{% hint style="warning" %}
**Important:** Keep your credentials secure and never commit them to version control. Consider using environment variables or a secrets management system.
{% endhint %}

### Private API  Documentation

Explore our interactive Swagger documentation to understand the available endpoints, request schemas, and response models for each module.

<table data-view="cards"><thead><tr><th></th><th data-type="content-ref"></th></tr></thead><tbody><tr><td>Core API</td><td><a href="https://api.catapa.com/core/swagger/swagger-ui/index.html">https://api.catapa.com/core/swagger/swagger-ui/index.html</a></td></tr><tr><td>Time Management</td><td><a href="https://api.catapa.com/timemanagement/swagger/swagger-ui/index.html">https://api.catapa.com/timemanagement/swagger/swagger-ui/index.html</a></td></tr><tr><td>Recruitment</td><td><a href="https://api.catapa.com/recruitment/swagger/swagger-ui/index.html">https://api.catapa.com/recruitment/swagger/swagger-ui/index.html</a></td></tr></tbody></table>

### Tutorials

More intermediate examples to help you learn the SDK.

#### Tutorial 1: Complete CRUD Operations

A complete example showing how to perform Create, Read, Update, and Delete operations.

```python
import random
import string

from catapa_private import CatapaPrivate

def main() -> None:
    """Main function demonstrating CRUD operations."""
    client = CatapaPrivate(
        tenant="zfrl",
        username="demo",
        password="dmo-password"
    )

    # Step 1: GET - Retrieve list of countries
    response = client.get("/core/countries", params={"page": 0, "size": 10})
    response.raise_for_status()
    data = response.json()
    countries = data.get("content", [])
    print(f"Found {len(countries)} countries")

    # Step 2: POST - Create a new country
    random_suffix = "".join(random.choices(string.ascii_lowercase + string.digits, k=6))
    new_country = {
        "code": f"IDN{random_suffix[:3].upper()}",
        "callingCode": "+62",
        "name": f"Indonesia_{random_suffix}",
        "taxTreaty": True,
    }
    response = client.post("/core/countries", json=new_country)
    response.raise_for_status()
    created_country = response.json()
    country_id = created_country.get("id")
    print(f"Created country: {created_country.get('name')} (ID: {country_id})")

    # Step 3: GET - Retrieve the created country by ID
    response = client.get(f"/core/countries/{country_id}")
    response.raise_for_status()
    retrieved_country = response.json()
    print(f"Retrieved country: {retrieved_country.get('name')}")

    # Step 4: PUT - Update the country
    retrieved_country["name"] = f"Indonesia_{random_suffix}_updated"
    response = client.put(f"/core/countries/{country_id}", json=retrieved_country)
    response.raise_for_status()
    updated_country = response.json()
    print(f"Updated country: {updated_country.get('name')}")

    # Step 5: DELETE - Delete the country
    payload = [{"id": country_id}]
    response = client.delete("/core/countries", json=payload)
    response.raise_for_status()
    print(f"Deleted country (ID: {country_id})")

if __name__ == "__main__":
    main()
```

#### Tutorial 2: Error Handling and Response Management

A complete example showing how to handle errors and manage responses properly.

```python
from catapa_private import CatapaPrivate
from requests.exceptions import HTTPError, RequestException

def main() -> None:
    """Main function demonstrating error handling."""
    client = CatapaPrivate(
        tenant="zfrl",
        username="demo",
        password="dmo-password"
    )

    try:
        response = client.get("/core/countries", params={"page": 0, "size": 10})
        response.raise_for_status()
        data = response.json()
        print(f"Success: Retrieved {len(data.get('content', []))} countries")
    except HTTPError as e:
        print(f"HTTP Error {e.response.status_code}: {e.response.text}")
    except RequestException as e:
        print(f"Request failed: {e}")

if __name__ == "__main__":
    main()
```

### Cookbook

Intermediate to Advanced examples for real-world scenarios. In this scenario we try to making concurrent API calls efficiently using the SDK.

```python
from catapa_private import CatapaPrivate
from concurrent.futures import ThreadPoolExecutor, as_completed

def main() -> None:
    """Main function for concurrent API calls example."""
    client = CatapaPrivate(
        tenant="zfrl",
        username="demo",
        password="dmo-password"
    )

    # Define API call functions
    def get_countries():
        response = client.get("/core/countries", params={"page": 0, "size": 10})
        response.raise_for_status()
        return response.json()

    def get_banks():
        response = client.get("/core/banks", params={"page": 0, "size": 10})
        response.raise_for_status()
        return response.json()

    def get_companies():
        response = client.get("/core/companies", params={"page": 0, "size": 10})
        response.raise_for_status()
        return response.json()

    # Execute API calls concurrently
    with ThreadPoolExecutor(max_workers=3) as executor:
        futures = {
            executor.submit(get_countries): "countries",
            executor.submit(get_banks): "banks",
            executor.submit(get_companies): "companies"
        }

        results = {}
        for future in as_completed(futures):
            task_name = futures[future]
            try:
                results[task_name] = future.result()
                print(f"✅ {task_name} retrieved successfully")
            except Exception as e:
                print(f"❌ {task_name} failed: {e}")

    # Use the results
    if "countries" in results:
        data = results["countries"]
        print(f"Countries: {len(data.get('content', []))}")
    if "banks" in results:
        data = results["banks"]
        print(f"Banks: {len(data.get('content', []))}")
    if "companies" in results:
        data = results["companies"]
        print(f"Companies: {len(data.get('content', []))}")

if __name__ == "__main__":
    main()
```

### Requirements

* Python 3.11+
