> ## Documentation Index
> Fetch the complete documentation index at: https://docs.veloiq.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Extend VeloIQ Modules with Custom API Endpoints

> Extend auto-generated CRUD with custom FastAPI endpoints using custom_api.py — no changes to main.py or the generated api.py required.

The auto-generated CRUD layer covers list, get, create, update, and delete for every model. For anything more specific — status transitions, batch operations, or domain-specific actions — create a `custom_api.py` file in the module directory. The framework scans for this file automatically on startup and registers any routes it finds, so you never need to touch `main.py`.

## When to use `custom_api.py`

Reach for `custom_api.py` when you need to:

* **Status transitions** — mark a task as complete, confirm an order, archive a record
* **Batch operations** — reassign multiple items at once, bulk-apply a tag
* **Domain-specific actions** — send a notification, trigger a workflow, compute a derived value

## Add custom endpoints to a module

<Steps>
  <Step title="Create custom_api.py in the module directory">
    Add the file alongside the generated files. For an `orders` module the layout looks like:

    ```
    modules/orders/
    ├── __init__.py
    ├── models.py
    ├── api.py          ← generated, do not edit
    └── custom_api.py   ← you create this
    ```
  </Step>

  <Step title="Import the router from api.py">
    Import `router` from `.api` — the generated router for this module. Do **not** import it from `veloiq_framework`; the router object must be the same instance the generator created so your routes are registered under the correct prefix.

    ```python theme={null}
    from .api import router
    ```
  </Step>

  <Step title="Add routes using standard FastAPI decorators">
    Decorate functions with `@router.post`, `@router.get`, and so on, exactly as you would in any FastAPI application. The full example below adds a `POST /{order_id}/confirm` endpoint and a `GET /pending` list:

    ```python theme={null}
    from fastapi import Depends, HTTPException
    from sqlmodel import Session
    from veloiq_framework import get_session
    from .api import router
    from .models import Order

    @router.post("/{order_id}/confirm")
    def confirm_order(order_id: int, session: Session = Depends(get_session)):
        order = session.get(Order, order_id)
        if order is None:
            raise HTTPException(404, f"Order {order_id} not found")
        order.status = "confirmed"
        session.add(order)
        session.commit()
        session.refresh(order)
        return order.model_dump()

    @router.get("/pending")
    def list_pending(session: Session = Depends(get_session)):
        rows = session.exec(select(Order).where(Order.status == "pending")).all()
        return [r.model_dump() for r in rows]
    ```
  </Step>

  <Step title="Verify the new endpoints appear in /docs">
    Restart the backend, or let uvicorn hot-reload pick up the file. Open `http://localhost:8000/docs` — the new endpoints appear under the module's section alongside the generated CRUD routes.
  </Step>
</Steps>

<Warning>
  Never edit the generated `api.py`. It is overwritten every time you run `veloiq generate`. Put all custom logic in `custom_api.py`.
</Warning>

## Task-manager example

For the task-manager tutorial app, create `backend/app/modules/tasks/custom_api.py` to add a "mark as complete" action:

```python theme={null}
from fastapi import Depends, HTTPException
from sqlmodel import Session

from veloiq_framework import get_session
from .api import router
from .models import Task


@router.post("/{task_id}/complete")
def complete_task(task_id: int, session: Session = Depends(get_session)):
    task = session.get(Task, task_id)
    if task is None:
        raise HTTPException(404, f"Task {task_id} not found")
    task.status = "done"
    session.add(task)
    session.commit()
    session.refresh(task)
    return task.model_dump()
```

After the backend reloads, `POST /task/{task_id}/complete` appears in `/docs` under the `task` section.

<Tip>
  Generate the `custom_api.py` stub automatically when you scaffold a module by passing the `--with-custom-api` flag:

  ```bash theme={null}
  veloiq add-module orders --with-custom-api
  ```
</Tip>
