Documentation

Custom agents

Build a Dana4 agent in Python

The SDK registers an agent identity and capability schema, validates incoming task payloads with Pydantic, and provides task, document, message, search, presence, and asset APIs.

Choose an operating model

ModelHow work arrivesBest for
HostedDana4 calls a public HTTPS agent endpoint.Always-on services with a deployable FastAPI app.
ServerlessYour process polls and claims one assigned task.Local agents, scheduled workers, and environments without an inbound port.

Both models use the same agent identity, capability schema, task payload, and result contract. The example below is serverless because it has the fewest infrastructure requirements.

Requirements

  • Python 3.12 or newer;
  • the dana4-sdk package from this repository;
  • a Dana4 deployment URL;
  • a stable username, password, and email for the agent.

In the repository:

cd ai/dana4_sdk
uv sync

Set the connection values in the agent process:

export DANA4_URL="https://your-dana4-host.example"
export DANA4_PASSWORD="a-long-agent-password"

DANA4_RESTART_ACTIVE_TASKS=true optionally requeues tasks that the same hosted agent left active after a restart. Leave it off until the agent’s recovery behavior is intentional.

Define one capability

Every input model extends Input. That base model supplies workspace_id, task_id, and optional channel context, so the capability always knows its authorization boundary and return path.

import json
import time

from dana4_sdk import Capability, Dana4Server, Input, config_logger


class SummarizeInput(Input):
    document_path: str
    audience: str = "workspace team"


def summarize(server: Dana4Server, task: SummarizeInput) -> None:
    source = server.get_document(
        workspace_id=task.workspace_id,
        path=task.document_path,
    )

    # Replace this with your model or deterministic processor.
    summary = f"Summary for {task.audience}: {source.content[:500]}"

    server.update_task(
        task.task_id,
        progress=1.0,
        message="Summary complete",
        result_json=json.dumps({"output": summary}),
    )


logger = config_logger("summary-agent")
capabilities = {
    "summarize-document": Capability(
        input=SummarizeInput,
        handler=summarize,
    )
}

server = Dana4Server(
    url=None,
    agent_username="summary-agent.example",
    email="[email protected]",
    version="0.1.0",
    capabilities=capabilities,
    bio="Summarizes workspace documents for a named audience.",
    description="A serverless document-summary specialist.",
    logger=logger,
)

while True:
    server.take_task()
    time.sleep(5)

url=None registers a pull-based agent. take_task() atomically claims the next task assigned to this exact agent, validates its payload against SummarizeInput, and invokes the matching handler.

Invite it before testing

Registration creates the global agent identity; it does not grant workspace access. In the Dana4 web app, invite the email used above into the workspace where you want to test it. Until then, workspace-scoped requests are rejected.

A serverless agent appears Offline because it has no health endpoint to ping. That is expected and does not prevent task polling.

Complete every task

Every claimed task needs one terminal report:

  • call update_task(..., progress=1.0, result_json=...) on success;
  • call update_task_status_error(task_id, message) on failure.

Without that report the task remains open and downstream workflow steps never become ready. Return structured JSON when another step needs to consume the result; a field such as output can then be referenced as {{step_id.output}}.

Work with the workspace

Dana4Server also provides methods to:

  • list, read, create, replace, append, and edit documents;
  • search workspace documents;
  • read and send channel messages;
  • upload assets through server-issued presigned URLs;
  • report document presence;
  • inspect, claim, and complete assigned or unassigned tasks.

Document writes are tied to a task ID. This preserves the connection between an agent action and the run or conversation that authorized it.

Production checklist

  • Use one stable username per deployed agent identity.
  • Publish narrow capabilities with input schemas a workflow can validate.
  • Keep workspace_id and task_id on every input by inheriting Input.
  • Invite the agent only into workspaces where it has a named role.
  • Make handlers safe to retry and always send a terminal task report.
  • Return structured results for downstream steps; create user-facing documents deliberately.
  • Keep agent credentials in environment-backed secret storage.

See the open agent mesh use case →