Metadata-Version: 2.4
Name: aqtos
Version: 0.1.1
Summary: Python SDK for the Aqtos project management API
License: MIT
Keywords: api,aqtos,project-management,sdk
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Requires-Dist: httpx>=0.27.0
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Description-Content-Type: text/markdown

<p align="center">
  <img src="https://aqtos.com/wp-content/uploads/2026/04/aqtos_logo.jpeg" alt="Aqtos" width="200" />
</p>

# aqtos-sdk

Python SDK for the [Aqtos](https://aqtos.com) project management API.

## Installation

```bash
pip install aqtos-sdk
```

## Quick start

```python
from aqtos import AqtosClient

with AqtosClient(domain="mycompany", api_key="ak_live_xxx") as client:
    task_id = client.tasks.create(
        project_id="proj-123",
        title="Deploy v2.0",
    )
```

`domain` is your Aqtos subdomain — e.g. `"mycompany"` for `mycompany.aqtos.io`.  
Your API key is sent automatically as the `X-API-KEY` header on every request.

## Tasks

### Create

```python
from aqtos import AqtosClient, Priority
from datetime import datetime

with AqtosClient(domain="mycompany", api_key="ak_live_xxx") as client:
    task_id = client.tasks.create(
        project_id="proj-123",
        title="Set up CI pipeline",
        # all keyword arguments below are optional
        description="Configure GitHub Actions for automated deploys.",
        priority=Priority.HIGH,
        status_id="status-todo",
        category_id="cat-engineering",
        assignees=["person-456", "person-789"],
        due_date=datetime(2026, 5, 1),
        start_date=datetime(2026, 4, 15),
        tags=["devops", "automation"],
        milestone_id="milestone-q2",
        estimated_duration_days=5,
        budget_type="HOURS",
        estimated_value=40,
    )
```

Returns the new task ID as a string.

### Retrieve

```python
task = client.tasks.get("task-uuid-001")

task.id          
task.title        
task.priority     
task.done         
task.tags         
task.assignee_ids 
task.raw          
```

### List

```python
tasks = client.tasks.list()

# with filters
tasks = client.tasks.list(projectId="proj-123", page=0, size=20)
```

### Update status

```python
client.tasks.update_status("task-uuid-001", status_id="status-in-review")


client.tasks.update_status("task-uuid-001", status_id="status-done", update_dependencies=True)
```

### Update description

```python
client.tasks.update_description(
    task_id="task-uuid-001",
    project_id="proj-123",
    description="Updated scope: include staging and production.",
)
```

### Bulk update

Only fields you explicitly pass are updated — the SDK sets the underlying CQRS update flags automatically.

```python
client.tasks.update(
    task_ids=["task-uuid-001", "task-uuid-002"],
    priority=Priority.CRITICAL,
    due_date=datetime(2026, 4, 30),
    status_id="status-in-progress",
    category_id="cat-backend",
)
```

### Assign

```python
client.tasks.assign("task-uuid-001", person_id="person-456")
```

### Mark done / cancel

```python
client.tasks.mark_done("task-uuid-001")

client.tasks.cancel("task-uuid-001")
client.tasks.cancel("task-uuid-001", update_dependencies=True)
```

### Checklist items

```python
# add
client.tasks.add_item("task-uuid-001", title="Write unit tests", assignees=["person-456"])

# edit
client.tasks.edit_item(
    "task-uuid-001",
    item_id="item-uuid-001",
    title="Write unit + integration tests",
    assignees=["person-789"],
)
```

## Async client

Every method has an async counterpart via `AsyncAqtosClient`:

```python
import asyncio
from aqtos import AsyncAqtosClient, Priority

async def main():
    async with AsyncAqtosClient(domain="mycompany", api_key="ak_live_xxx") as client:
        task_id = await client.tasks.create(
            project_id="proj-123",
            title="Async task",
            priority=Priority.MEDIUM,
        )
        await client.tasks.mark_done(task_id)

asyncio.run(main())
```

## Error handling

```python
from aqtos import (
    AqtosAuthError,
    AqtosNotFoundError,
    AqtosValidationError,
    AqtosServerError,
    AqtosAPIError,
)

try:
    task = client.tasks.get("nonexistent-id")
except AqtosAuthError:
    # 401 / 403 — invalid or missing API key
    ...
except AqtosNotFoundError:
    # 404 — resource does not exist
    ...
except AqtosValidationError as e:
    # 400 / 422 — bad request payload
    print(e.response_body)
except AqtosServerError as e:
    # 5xx — upstream server error
    print(e.status_code)
except AqtosAPIError as e:
    # catch-all for any other HTTP error
    print(e.status_code, e.response_body)
```

All exceptions expose `status_code` (int) and `response_body` (parsed JSON or raw text).

## Reference

### `AqtosClient(domain, api_key, *, timeout=30, http_client=None)`

| Parameter | Type | Description |
|---|---|---|
| `domain` | `str` | Aqtos subdomain (e.g. `"mycompany"`) |
| `api_key` | `str` | API key sent as `X-API-KEY` |
| `timeout` | `float` | Request timeout in seconds |
| `http_client` | `httpx.Client` | Optional pre-configured client |

`AsyncAqtosClient` accepts the same parameters with an optional `httpx.AsyncClient`.

### `Priority`

`LOW` · `MEDIUM` · `HIGH` · `CRITICAL`

### `BudgetType`

`MONEY` · `HOURS` · `DAYS`
