Skip to main content

hai-agent-api

Shared FastAPI surface for the agents API. This package defines the HTTP routes, request/response models, and authentication dependencies. Your application supplies the backing logic by implementing the service protocols and passing them to create_router.

Install

uv pip install hai-agent-api

You also need an ASGI server to run locally, for example:

uv pip install "uvicorn[standard]"

Quick start

hai-agent-api does not ship a runnable server. Wire the router into your own FastAPI app and provide implementations for all five services.

# app.py
from agent_api import ApiConfig, AuthConfig, Services, create_router, install_exception_handlers
from agent_api.user import ApiUser
from agent_interface.specs.skill import Skill
from agp_types import Page, PageRequest
from fastapi import FastAPI, HTTPException

# ---------------------------------------------------------------------------
# Example service implementations
# ---------------------------------------------------------------------------

class InMemorySkillService:
    """Minimal skill catalog backed by an in-memory dict."""

    def __init__(self) -> None:
        self._skills: dict[str, Skill] = {}

    async def create_skill(self, user: ApiUser, create: Skill) -> Skill:
        if create.name in self._skills:
            raise HTTPException(status_code=409, detail=f"Skill {create.name!r} already exists.")
        self._skills[create.name] = create
        return create

    async def get_page(
        self,
        user: ApiUser,
        page_request: PageRequest,
        *,
        name: str | None = None,
        search: str | None = None,
    ) -> Page[Skill]:
        items = list(self._skills.values())
        if name:
            items = [s for s in items if name.lower() in s.name.lower()]
        if search:
            q = search.lower()
            items = [s for s in items if q in s.name.lower() or q in (s.description or "").lower()]
        return Page[Skill](items=items, page=page_request.page, size=page_request.size, total=len(items))

    async def get_skill(self, user: ApiUser, name: str) -> Skill:
        skill = self._skills.get(name)
        if skill is None:
            raise HTTPException(status_code=404, detail=f"Skill {name!r} not found.")
        return skill

    async def update_skill(self, user: ApiUser, name: str, update: Skill) -> Skill:
        if update.name != name:
            raise HTTPException(status_code=422, detail="update.name must match the path name.")
        if name not in self._skills:
            raise HTTPException(status_code=404, detail=f"Skill {name!r} not found.")
        self._skills[name] = update
        return update

    async def delete_skill(self, user: ApiUser, name: str) -> None:
        if name not in self._skills:
            raise HTTPException(status_code=404, detail=f"Skill {name!r} not found.")
        del self._skills[name]


class StubService:
    """Placeholder for services you have not implemented yet."""

    def __getattr__(self, name: str):
        async def _not_implemented(*_args, **_kwargs):
            raise HTTPException(status_code=501, detail=f"{name} is not implemented.")

        return _not_implemented


# ---------------------------------------------------------------------------
# FastAPI app
# ---------------------------------------------------------------------------

app = FastAPI(title="agents-api", version="0.0.0")

services = Services(
    agents=StubService(),
    environments=StubService(),
    skills=InMemorySkillService(),
    sessions=StubService(),
    memories=StubService(),
    webhooks=StubService(),
)

app.include_router(
    create_router(
        services,
        config=ApiConfig(auth=AuthConfig(mode="local")),
    ),
    prefix="/api",
)
install_exception_handlers(app)

Run the server:

uvicorn app:app --reload --port 8000

Open the interactive docs at http://localhost:8000/docs. Skill endpoints are available under /api/v2/skills; unimplemented services return 501 Not Implemented.

Try a request

curl -s -X POST http://localhost:8000/api/v2/skills \
  -H 'Content-Type: application/json' \
  -d '{"name": "my-skill", "description": "Example", "body": "Do the thing."}' | jq

Service protocols

Implement these protocols (structural typing—no base class required) and pass them in a Services container:

Protocol Router prefix Responsibility
AgentServiceProtocol /v2/agents Agent catalog CRUD and spec resolution
EnvironmentServiceProtocol /v2/environments Environment catalog CRUD
SkillServiceProtocol /v2/skills Skill catalog CRUD
SessionServiceProtocol /v2/sessions Agentic session lifecycle, events, quota
MemoryServiceProtocol /v2/memories Per-org key/value memory (hidden from OpenAPI)
WebhookServiceProtocol /v2/webhooks Webhook subscription CRUD and delivery history
ScheduleServiceProtocol /v2/schedules Cron schedules that create sessions; optional — the tree mounts only when wired (soft launch, hidden from OpenAPI)

Full method signatures live in agent_api.services.protocols. Raise fastapi.HTTPException for expected client errors (404, 409, 422, etc.). Domain exceptions such as agent_api.exceptions.IdempotencyKeyConflict are mapped to the standard error body when you call install_exception_handlers(app) on your FastAPI app (Agent Platform does this in hplatform.app).

Error responses

All API errors use a single JSON shape:

{
  "message": "Human-readable summary",
  "detail": ["..."]
}
  • message is a short summary string.
  • detail is always an array. For validation failures it contains the usual Pydantic error objects; for application errors it contains objects with type and message.

Pydantic request validation, HTTPException, and registered domain exceptions are all normalized to this format by install_exception_handlers.

Configuration

ApiConfig controls router behaviour:

Field Default Purpose
long_poll_max_wait_for_seconds 60 Upper bound for long-poll on /sessions/{id}/changes
session_share_url_template /share/api/v1/trajectories/{session_id} Template for public share links
auth AuthConfig(mode="platform") How requests are authenticated (see below)

Authentication

Pass AuthConfig via ApiConfig.auth when calling create_router:

Mode Use case Behaviour
platform Agent Platform (default) Requires X-User-Sub and X-User-Org headers injected by the gateway; returns 401 when missing
local Local development / standalone apps Protected routes accept optional identity headers and fall back to default UUIDs when omitted

Local mode defaults:

Field Default
default_user_id 00000000-0000-0000-0000-000000000001
default_org_id 00000000-0000-0000-0000-000000000002
default_email local-dev@example.com

Override headers when you need a specific identity:

curl -s http://localhost:8000/api/v2/skills \
  -H 'X-User-Sub: 11111111-1111-1111-1111-111111111111' \
  -H 'X-User-Org: 22222222-2222-2222-2222-222222222222'

Agent Platform keeps the default platform mode—no change required in production wiring.

API layout

create_router returns a router with this structure:

/v2/sessions/...
/v2/memories/...
/v2/skills/...
/v2/environments/...
/v2/agents/...
/v2/webhooks/...
/v2/schedules/...   (only when Services.schedules is provided)

Mount it under your chosen prefix (commonly /api, yielding /api/v2/...).

Metadata

Release files for hai-agent-api 0.1.99

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for hai-agent-api 0.1.99
File Interpreter ABI Platform
hai_agent_api-0.1.99-py3-none-any.whl Python 3 none any Details

Release files / hai_agent_api-0.1.99-py3-none-any.whl

Download URL hai_agent_api-0.1.99-py3-none-any.whl
Size 79.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
23c5de50407d924fe663cbf4ead451eb97c204c38173c32b7cae549d0de61054
BLAKE2b-256 checksum
How to use checksums
7e2f9b91691f70e1df97aed687b777be98eeb7d7eeedd48131b3c4c97114cb94
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 19, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.127

1 release file

0.1.126

1 release file

0.1.125

1 release file

0.1.124

1 release file

0.1.123

1 release file

0.1.122

1 release file

0.1.121

1 release file

0.1.120

1 release file

0.1.119

1 release file

0.1.118

1 release file

0.1.117

1 release file

0.1.116

1 release file

0.1.115

1 release file

0.1.114

1 release file

0.1.113

1 release file

0.1.112

1 release file

0.1.111

1 release file

0.1.110

1 release file

0.1.109

1 release file

0.1.108

1 release file

0.1.107

1 release file

0.1.106

1 release file

0.1.105

1 release file

0.1.104

1 release file

0.1.103

1 release file

0.1.102

1 release file

0.1.101

1 release file

0.1.100

1 release file

This release

0.1.99 This release

1 release file

0.1.98

1 release file

0.1.97

1 release file

0.1.96

1 release file

0.1.95

1 release file

0.1.94

1 release file

0.1.93

1 release file

0.1.92

1 release file

0.1.91

1 release file

0.1.90

1 release file

0.1.89

1 release file

0.1.88

1 release file

0.1.87

1 release file

0.1.86

1 release file

0.1.85

1 release file

0.1.84

1 release file

0.1.83

1 release file

0.1.82

1 release file

0.1.81

1 release file

0.1.80

1 release file

0.1.79

1 release file

0.1.78

1 release file

0.1.77

1 release file

0.1.76

1 release file

0.1.75

1 release file

0.1.74

1 release file

0.1.73

1 release file

0.1.72

1 release file

0.1.71

1 release file

0.1.70

1 release file

0.1.69

1 release file

0.1.68

1 release file

0.1.67

1 release file

0.1.66

1 release file

0.1.65

1 release file

0.1.64

1 release file

0.1.63

1 release file

0.1.62

1 release file

0.1.61

1 release file

0.1.60

1 release file

0.1.59

1 release file

0.1.58

1 release file

0.1.57

1 release file

0.1.56

1 release file

0.1.55

1 release file

0.1.54

1 release file

0.1.53

1 release file

0.1.52

1 release file

0.1.51

1 release file

0.1.50

1 release file

0.1.49

1 release file

0.1.48

1 release file

0.1.47

1 release file

0.1.46

1 release file

0.1.45

1 release file

0.1.44

1 release file

0.1.43

1 release file

0.1.42

1 release file

0.1.41

1 release file

0.1.40

1 release file

0.1.39

1 release file

0.1.38

1 release file

0.1.37

1 release file

0.1.36

1 release file

0.1.35

1 release file

0.1.34

1 release file

0.1.33

1 release file

0.1.32

1 release file

0.1.31

1 release file

0.1.30

1 release file

0.1.29

1 release file

0.1.28

1 release file

0.1.27

1 release file

0.1.26

1 release file

0.1.25

1 release file

0.1.24

1 release file

0.1.23

1 release file

0.1.22

1 release file

0.1.21

1 release file

0.1.20

1 release file

0.1.19

1 release file

0.1.18

1 release file

0.1.17

1 release file

0.1.16

1 release file

0.1.15

1 release file

0.1.14

1 release file

0.1.13

1 release file

0.1.12

1 release file

0.1.11

1 release file

0.1.10

1 release file

0.1.9

1 release file

0.1.8

1 release file

0.1.7

1 release file

0.1.6

1 release file

0.1.5

1 release file

0.1.4

1 release file

0.1.3

1 release file

0.1.2

1 release file

0.1.1

1 release file

0.1.0

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page