ottonova

ottonova – Agentic AI Platform on AWS

Pilot test environment

This is a test environment of an AI assistant. It answers your questions in a chat and hands parts of the work to specialised agents and tools. Nothing here is a live ottonova service: try it, push its limits and tell us what you notice.

Your data

This is a test environment. Do not enter real personal or health data. Conversations are stored, and admins can read them.

How to test

  1. Sign in. Select Sign in at the top right (or open the chat icon, which asks you to sign in first). Enter a user name and the password from the table of test accounts below.
  2. Open the chat. Select the chat icon at the bottom right of the page. The ottonova AI Assistant opens in a window you can move by its title bar, resize from its corners, expand to a large view and minimise back to the icon.
  3. Ask a question. Type it into the message box and select Send. The answer appears word by word. The first answer after a pause can take several seconds, because the system wakes up first.

Sign out and switch user

Select Sign out at the top right. Then select Sign in and sign in with another account from the table. Every account has its own conversations.

Test accounts

Test accounts
User namePasswordRoleWhat this user can doWhat is refused
lenaVd5#BBUiqwO6rHWWxvTWlSEy7igK0nzacustomer
  • Chat with the AI Assistant
  • See only your own data (party P001)
  • Find parties (customers and their family members) by a part of their name or insurance number; a customer finds only themselves (search_party)
  • Overview of one party by party id (P…) (get_customer_360)
  • One insurance policy by id (POL-…) (get_policy)
  • One claim by id (CLM-…) (get_claim)
  • The documents (metadata only, never the content) of a party, an insurance policy, a claim, a SEPA mandate or a tariff, by its id or tariff name (list_documents)
  • The tariff benefits of one tariff by its name (such as `Comfort Class`) (get_tariff_benefits)
  • Whether an insurance policy (POL-…) covers a benefit code (such as `physiotherapy`) (check_coverage)
  • Search the tariff clauses (the AVB general terms) of one tariff for a word or a `§` reference, such as `IGeL` or `§3.2` (search_tariff_clauses)
  • The premium payments of one insurance policy (POL-…), the latest due date first, optionally only `collected` or `overdue` ones (list_premium_payments)
  • Hand a claim (CLM-…) to a human team (escalate_to_human)
  • Remember what you tell it across conversations
  • The escalation queue (get_open_escalations)
  • Record a reviewer's decision on an escalated claim (CLM-…) (resume_claim)
  • Insights and the LiteLLM admin (admin role only)
denizVd5#BBUiqwO6rHWWxvTWlSEy7igK0nzacustomer
  • Chat with the AI Assistant
  • See only your own data (party P002)
  • Find parties (customers and their family members) by a part of their name or insurance number; a customer finds only themselves (search_party)
  • Overview of one party by party id (P…) (get_customer_360)
  • One insurance policy by id (POL-…) (get_policy)
  • One claim by id (CLM-…) (get_claim)
  • The documents (metadata only, never the content) of a party, an insurance policy, a claim, a SEPA mandate or a tariff, by its id or tariff name (list_documents)
  • The tariff benefits of one tariff by its name (such as `Comfort Class`) (get_tariff_benefits)
  • Whether an insurance policy (POL-…) covers a benefit code (such as `physiotherapy`) (check_coverage)
  • Search the tariff clauses (the AVB general terms) of one tariff for a word or a `§` reference, such as `IGeL` or `§3.2` (search_tariff_clauses)
  • The premium payments of one insurance policy (POL-…), the latest due date first, optionally only `collected` or `overdue` ones (list_premium_payments)
  • Hand a claim (CLM-…) to a human team (escalate_to_human)
  • Remember what you tell it across conversations
  • The escalation queue (get_open_escalations)
  • Record a reviewer's decision on an escalated claim (CLM-…) (resume_claim)
  • Insights and the LiteLLM admin (admin role only)
giuliaVd5#BBUiqwO6rHWWxvTWlSEy7igK0nzacustomer
  • Chat with the AI Assistant
  • See only your own data (party P003)
  • Find parties (customers and their family members) by a part of their name or insurance number; a customer finds only themselves (search_party)
  • Overview of one party by party id (P…) (get_customer_360)
  • One insurance policy by id (POL-…) (get_policy)
  • One claim by id (CLM-…) (get_claim)
  • The documents (metadata only, never the content) of a party, an insurance policy, a claim, a SEPA mandate or a tariff, by its id or tariff name (list_documents)
  • The tariff benefits of one tariff by its name (such as `Comfort Class`) (get_tariff_benefits)
  • Whether an insurance policy (POL-…) covers a benefit code (such as `physiotherapy`) (check_coverage)
  • Search the tariff clauses (the AVB general terms) of one tariff for a word or a `§` reference, such as `IGeL` or `§3.2` (search_tariff_clauses)
  • The premium payments of one insurance policy (POL-…), the latest due date first, optionally only `collected` or `overdue` ones (list_premium_payments)
  • Hand a claim (CLM-…) to a human team (escalate_to_human)
  • Remember what you tell it across conversations
  • The escalation queue (get_open_escalations)
  • Record a reviewer's decision on an escalated claim (CLM-…) (resume_claim)
  • Insights and the LiteLLM admin (admin role only)
pawelVd5#BBUiqwO6rHWWxvTWlSEy7igK0nzacustomer
  • Chat with the AI Assistant
  • See only your own data (party P004)
  • Find parties (customers and their family members) by a part of their name or insurance number; a customer finds only themselves (search_party)
  • Overview of one party by party id (P…) (get_customer_360)
  • One insurance policy by id (POL-…) (get_policy)
  • One claim by id (CLM-…) (get_claim)
  • The documents (metadata only, never the content) of a party, an insurance policy, a claim, a SEPA mandate or a tariff, by its id or tariff name (list_documents)
  • The tariff benefits of one tariff by its name (such as `Comfort Class`) (get_tariff_benefits)
  • Whether an insurance policy (POL-…) covers a benefit code (such as `physiotherapy`) (check_coverage)
  • Search the tariff clauses (the AVB general terms) of one tariff for a word or a `§` reference, such as `IGeL` or `§3.2` (search_tariff_clauses)
  • The premium payments of one insurance policy (POL-…), the latest due date first, optionally only `collected` or `overdue` ones (list_premium_payments)
  • Hand a claim (CLM-…) to a human team (escalate_to_human)
  • Remember what you tell it across conversations
  • The escalation queue (get_open_escalations)
  • Record a reviewer's decision on an escalated claim (CLM-…) (resume_claim)
  • Insights and the LiteLLM admin (admin role only)
amelieVd5#BBUiqwO6rHWWxvTWlSEy7igK0nzacustomer
  • Chat with the AI Assistant
  • See only your own data (party P005)
  • Find parties (customers and their family members) by a part of their name or insurance number; a customer finds only themselves (search_party)
  • Overview of one party by party id (P…) (get_customer_360)
  • One insurance policy by id (POL-…) (get_policy)
  • One claim by id (CLM-…) (get_claim)
  • The documents (metadata only, never the content) of a party, an insurance policy, a claim, a SEPA mandate or a tariff, by its id or tariff name (list_documents)
  • The tariff benefits of one tariff by its name (such as `Comfort Class`) (get_tariff_benefits)
  • Whether an insurance policy (POL-…) covers a benefit code (such as `physiotherapy`) (check_coverage)
  • Search the tariff clauses (the AVB general terms) of one tariff for a word or a `§` reference, such as `IGeL` or `§3.2` (search_tariff_clauses)
  • The premium payments of one insurance policy (POL-…), the latest due date first, optionally only `collected` or `overdue` ones (list_premium_payments)
  • Hand a claim (CLM-…) to a human team (escalate_to_human)
  • Remember what you tell it across conversations
  • The escalation queue (get_open_escalations)
  • Record a reviewer's decision on an escalated claim (CLM-…) (resume_claim)
  • Insights and the LiteLLM admin (admin role only)
rev017Vd5#BBUiqwO6rHWWxvTWlSEy7igK0nzareviewer
  • Chat with the AI Assistant
  • See every customer's data
  • Find parties (customers and their family members) by a part of their name or insurance number; a customer finds only themselves (search_party)
  • Overview of one party by party id (P…) (get_customer_360)
  • One insurance policy by id (POL-…) (get_policy)
  • One claim by id (CLM-…) (get_claim)
  • The documents (metadata only, never the content) of a party, an insurance policy, a claim, a SEPA mandate or a tariff, by its id or tariff name (list_documents)
  • The tariff benefits of one tariff by its name (such as `Comfort Class`) (get_tariff_benefits)
  • Whether an insurance policy (POL-…) covers a benefit code (such as `physiotherapy`) (check_coverage)
  • Search the tariff clauses (the AVB general terms) of one tariff for a word or a `§` reference, such as `IGeL` or `§3.2` (search_tariff_clauses)
  • The premium payments of one insurance policy (POL-…), the latest due date first, optionally only `collected` or `overdue` ones (list_premium_payments)
  • The escalation queue (get_open_escalations)
  • Record a reviewer's decision on an escalated claim (CLM-…) (resume_claim)
  • Remember what you tell it across conversations
  • Hand a claim (CLM-…) to a human team (escalate_to_human)
  • Insights and the LiteLLM admin (admin role only)
rev021Vd5#BBUiqwO6rHWWxvTWlSEy7igK0nzareviewer
  • Chat with the AI Assistant
  • See every customer's data
  • Find parties (customers and their family members) by a part of their name or insurance number; a customer finds only themselves (search_party)
  • Overview of one party by party id (P…) (get_customer_360)
  • One insurance policy by id (POL-…) (get_policy)
  • One claim by id (CLM-…) (get_claim)
  • The documents (metadata only, never the content) of a party, an insurance policy, a claim, a SEPA mandate or a tariff, by its id or tariff name (list_documents)
  • The tariff benefits of one tariff by its name (such as `Comfort Class`) (get_tariff_benefits)
  • Whether an insurance policy (POL-…) covers a benefit code (such as `physiotherapy`) (check_coverage)
  • Search the tariff clauses (the AVB general terms) of one tariff for a word or a `§` reference, such as `IGeL` or `§3.2` (search_tariff_clauses)
  • The premium payments of one insurance policy (POL-…), the latest due date first, optionally only `collected` or `overdue` ones (list_premium_payments)
  • The escalation queue (get_open_escalations)
  • Record a reviewer's decision on an escalated claim (CLM-…) (resume_claim)
  • Remember what you tell it across conversations
  • Hand a claim (CLM-…) to a human team (escalate_to_human)
  • Insights and the LiteLLM admin (admin role only)
maraVd5#BBUiqwO6rHWWxvTWlSEy7igK0nzaback_office
  • Chat with the AI Assistant
  • See every customer's data
  • Find parties (customers and their family members) by a part of their name or insurance number; a customer finds only themselves (search_party)
  • Overview of one party by party id (P…) (get_customer_360)
  • One insurance policy by id (POL-…) (get_policy)
  • One claim by id (CLM-…) (get_claim)
  • The documents (metadata only, never the content) of a party, an insurance policy, a claim, a SEPA mandate or a tariff, by its id or tariff name (list_documents)
  • The tariff benefits of one tariff by its name (such as `Comfort Class`) (get_tariff_benefits)
  • Whether an insurance policy (POL-…) covers a benefit code (such as `physiotherapy`) (check_coverage)
  • Search the tariff clauses (the AVB general terms) of one tariff for a word or a `§` reference, such as `IGeL` or `§3.2` (search_tariff_clauses)
  • The premium payments of one insurance policy (POL-…), the latest due date first, optionally only `collected` or `overdue` ones (list_premium_payments)
  • The escalation queue (get_open_escalations)
  • Hand a claim (CLM-…) to a human team (escalate_to_human)
  • Remember what you tell it across conversations
  • Record a reviewer's decision on an escalated claim (CLM-…) (resume_claim)
  • Insights and the LiteLLM admin (admin role only)
carolVd5#BBUiqwO6rHWWxvTWlSEy7igK0nzaadmin
  • Chat with the AI Assistant
  • See every customer's data
  • Find parties (customers and their family members) by a part of their name or insurance number; a customer finds only themselves (search_party)
  • Overview of one party by party id (P…) (get_customer_360)
  • One insurance policy by id (POL-…) (get_policy)
  • One claim by id (CLM-…) (get_claim)
  • The documents (metadata only, never the content) of a party, an insurance policy, a claim, a SEPA mandate or a tariff, by its id or tariff name (list_documents)
  • The tariff benefits of one tariff by its name (such as `Comfort Class`) (get_tariff_benefits)
  • Whether an insurance policy (POL-…) covers a benefit code (such as `physiotherapy`) (check_coverage)
  • Search the tariff clauses (the AVB general terms) of one tariff for a word or a `§` reference, such as `IGeL` or `§3.2` (search_tariff_clauses)
  • The premium payments of one insurance policy (POL-…), the latest due date first, optionally only `collected` or `overdue` ones (list_premium_payments)
  • The escalation queue (get_open_escalations)
  • Hand a claim (CLM-…) to a human team (escalate_to_human)
  • Record a reviewer's decision on an escalated claim (CLM-…) (resume_claim)
  • Remember what you tell it across conversations
  • Open Insights and the LiteLLM admin
Nothing
daveVd5#BBUiqwO6rHWWxvTWlSEy7igK0nzano role
  • Sign in and open the AI Assistant
  • Every chat request is refused by design: this user has no role

Example questions

“Hallo, was kannst du für mich tun?”
An answer in German: the assistant helps with claims, coverage and premium payments.
“Hello! What can you help me with?”
The same answer in English.
“I need help with something.”
A question back: the assistant asks what it is about.
“Is physiotherapy covered in my tariff?”
A general answer or a question back. The orchestrator answers alone for now: it reads no insurance policy yet.
“Remember that my favourite number is 7.”
A confirmation. Then select New conversation and ask “What is my favourite number?”: it should remember 7.

Signed in as carol, you can also open these addresses and sign in there with the same account:

If something does not work

Feedback

Send your notes to the person who shared this link with you: what you asked, what you expected, what happened.

Connect over A2A (for developers)

Other applications can talk to the assistant directly through its external door, an A2A agent: any A2A client or a plain curl works, with a personal API key.

Addresses

  • The root agent card — where A2A discovery looks first; publichttps://ottonova-ai-assistant-dev.jovial-ottonova.de/.well-known/agent-card.json
  • The orchestrator's agent card — the same card under the door; publichttps://ottonova-ai-assistant-dev.jovial-ottonova.de/a2a/orchestrator/.well-known/agent-card.json
  • The A2A endpoint — where every JSON-RPC call is posted; needs a keyhttps://ottonova-ai-assistant-dev.jovial-ottonova.de/a2a/orchestrator

Get a personal API key

  1. Sign in at the top right with a test account. The key acts as the account that created it: its role decides which agents and tools answer (see the table of test accounts), and an account without a role is refused.
  2. Create a key with the button below. The key is shown this once only: copy it right away. The same place revokes it.
  3. Send the key on every call to the door as the header Authorization: Bearer <YOUR-API-KEY>. The door refuses sign-in tokens and service tokens.
  4. Keep it secret. A key stays valid until it is revoked and has a daily budget of messages; past it the door answers BUDGET_EXCEEDED until the time in Retry-After.

Sign in at the top right first; the key will act as the account you sign in with.

From a script, the app backend creates a key on POST https://api-dev.jovial-ottonova.de/api-keys with your sign-in token as bearer; the answer holds the key, shown this once only, and its id, which DELETE https://api-dev.jovial-ottonova.de/api-keys/<id> revokes.

The sign-in token is the Cognito ID token of your signed-in session; on AWS every POST to the app backend also carries the SHA-256 of its body, here the empty one.

Create a key from a script
TOKEN='<YOUR-SIGN-IN-TOKEN>'
curl -s -X POST https://api-dev.jovial-ottonova.de/api-keys -H "Authorization: Bearer $TOKEN" \
  -H 'x-amz-content-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'

201 with id, key and prefix; the key is in this answer only.

Revoke a key
TOKEN='<YOUR-SIGN-IN-TOKEN>'
curl -s -X DELETE https://api-dev.jovial-ottonova.de/api-keys/<KEY-ID> -H "Authorization: Bearer $TOKEN"

204; from then on the door refuses the key.

Call the door

The door speaks A2A 1.0 over JSON-RPC 2.0 with the methods SendMessage, SendStreamingMessage (server-sent events), GetTask and CancelTask. Send the header A2A-Version: 1.0 and the question as one data part {"text": "…"}. On AWS every POST also carries the SHA-256 of its body in x-amz-content-sha256; locally the header is ignored.

Fetch the agent card
curl -s https://ottonova-ai-assistant-dev.jovial-ottonova.de/a2a/orchestrator/.well-known/agent-card.json

Public, no key: the card names the endpoint, the skills and the personalApiKey scheme.

Send a message
KEY='<YOUR-API-KEY>'
BODY=$(printf '{"jsonrpc":"2.0","id":"1","method":"SendMessage","params":{"message":{"messageId":"%s","role":"ROLE_USER","parts":[{"data":{"text":"Hallo, was kannst du für mich tun?"}}]}}}' "$(uuidgen)")
curl -s https://ottonova-ai-assistant-dev.jovial-ottonova.de/a2a/orchestrator \
  -H "Authorization: Bearer $KEY" \
  -H 'A2A-Version: 1.0' \
  -H 'Content-Type: application/json' \
  -H "x-amz-content-sha256: $(printf %s "$BODY" | shasum -a 256 | cut -d' ' -f1)" \
  --data "$BODY"

Waits for the answer: result.task with status.state TASK_STATE_COMPLETED and the orchestrator_response artifact, whose data holds kind and text.

Stream a message
KEY='<YOUR-API-KEY>'
BODY=$(printf '{"jsonrpc":"2.0","id":"1","method":"SendStreamingMessage","params":{"message":{"messageId":"%s","role":"ROLE_USER","parts":[{"data":{"text":"Hallo, was kannst du für mich tun?"}}]}}}' "$(uuidgen)")
curl -sN https://ottonova-ai-assistant-dev.jovial-ottonova.de/a2a/orchestrator \
  -H "Authorization: Bearer $KEY" \
  -H 'A2A-Version: 1.0' \
  -H 'Content-Type: application/json' \
  -H 'Accept: text/event-stream' \
  -H "x-amz-content-sha256: $(printf %s "$BODY" | shasum -a 256 | cut -d' ' -f1)" \
  --data "$BODY"

Server-sent events, each a JSON-RPC result: the task first, then artifactUpdate chunks of answer-stream, then a statusUpdate with TASK_STATE_COMPLETED.

Read a task back
KEY='<YOUR-API-KEY>'
BODY='{"jsonrpc":"2.0","id":"2","method":"GetTask","params":{"id":"<TASK-ID>"}}'
curl -s https://ottonova-ai-assistant-dev.jovial-ottonova.de/a2a/orchestrator \
  -H "Authorization: Bearer $KEY" \
  -H 'A2A-Version: 1.0' \
  -H 'Content-Type: application/json' \
  -H "x-amz-content-sha256: $(printf %s "$BODY" | shasum -a 256 | cut -d' ' -f1)" \
  --data "$BODY"

The task with its state and artifacts; CancelTask takes the same params to stop a running task.

Send a message with the a2a-sdk Python client
import asyncio, hashlib, uuid
import httpx
from a2a.client import ClientConfig, create_client
from a2a.helpers.proto_helpers import new_data_part
from a2a.types import a2a_pb2 as pb

async def add_body_hash(request: httpx.Request) -> None:
    request.headers["x-amz-content-sha256"] = hashlib.sha256(request.content).hexdigest()

async def main() -> None:
    http = httpx.AsyncClient(timeout=180, headers={"Authorization": "Bearer <YOUR-API-KEY>"},
                             event_hooks={"request": [add_body_hash]})
    client = await create_client("https://ottonova-ai-assistant-dev.jovial-ottonova.de", ClientConfig(httpx_client=http, streaming=False))
    message = pb.Message(message_id=str(uuid.uuid4()), role=pb.Role.ROLE_USER,
                         parts=[new_data_part({"text": "Hallo, was kannst du für mich tun?"})])
    async for response in client.send_message(pb.SendMessageRequest(message=message)):
        print(response.task.artifacts[0].parts[0].data)

asyncio.run(main())

pip install a2a-sdk httpx; prints the orchestrator's answer.

When a call is refused

A refused call answers with its HTTP status and one error envelope: code says what happened, message says it in a sentence, hint says what to do next, and retryable says whether sending the same call again can succeed.

An error envelope
{
  "code": "UNAUTHENTICATED",
  "message": "No valid token or key was sent.",
  "hint": "Send a valid service token, JWT or personal API key in the Authorization header.",
  "retryable": false,
  "details": {
    "reason": "personal API key required"
  }
}
Refusals of the external door
CodeHTTP statusWhenRetry?
UNAUTHENTICATED401No key, or a sign-in or service token instead of a key.No
KEY_NOT_FOUND404The key does not exist or was revoked.No
BUDGET_EXCEEDED429The key used up its daily budget; wait for Retry-After.Yes
UPSTREAM_UNAVAILABLE503A service behind the door does not answer.Yes

A JSON-RPC answer can also carry an error: -32001 for a task id another user created or none did. When the key's role may not use an agent, the task ends in TASK_STATE_FAILED with the error envelope in its status message.

Learn more