# 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+


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gdplabs.gitbook.io/catapa/developer-documentation/hris-private-api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
