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
- 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.
- 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.
- 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
| User name | Password | Role | What this user can do | What is refused |
|---|---|---|---|---|
| lena | Vd5#BBUiqwO6rHWWxvTWlSEy7igK0nza | customer |
|
|
| deniz | Vd5#BBUiqwO6rHWWxvTWlSEy7igK0nza | customer |
|
|
| giulia | Vd5#BBUiqwO6rHWWxvTWlSEy7igK0nza | customer |
|
|
| pawel | Vd5#BBUiqwO6rHWWxvTWlSEy7igK0nza | customer |
|
|
| amelie | Vd5#BBUiqwO6rHWWxvTWlSEy7igK0nza | customer |
|
|
| rev017 | Vd5#BBUiqwO6rHWWxvTWlSEy7igK0nza | reviewer |
|
|
| rev021 | Vd5#BBUiqwO6rHWWxvTWlSEy7igK0nza | reviewer |
|
|
| mara | Vd5#BBUiqwO6rHWWxvTWlSEy7igK0nza | back_office |
|
|
| carol | Vd5#BBUiqwO6rHWWxvTWlSEy7igK0nza | admin |
| Nothing |
| dave | Vd5#BBUiqwO6rHWWxvTWlSEy7igK0nza | 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.
For the admin account
Signed in as carol, you can also open these addresses and sign in there with the same account:
- Insights — usage, cost, quality and the stored conversations.
- LiteLLM admin — the model gateway: models, keys and spend.
If something does not work
- The page or the sign-in address can't be reached. Your network may not know the address yet. Wait a few minutes and try again, try another browser, or turn off your VPN.
- Sign-in keeps returning to the sign-in page. Clear the cookies and site data for this site, then sign in again.
- An error appears in the chat. Select Retry when it is offered, or send the message again. The error shows a reference code: copy it into your notes.
- Every chat request of dave is refused. That is by design: dave has no role.
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; public
https://ottonova-ai-assistant-dev.jovial-ottonova.de/.well-known/agent-card.json - The orchestrator's agent card — the same card under the door; public
https://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 key
https://ottonova-ai-assistant-dev.jovial-ottonova.de/a2a/orchestrator
Get a personal API key
- 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.
- Create a key with the button below. The key is shown this once only: copy it right away. The same place revokes it.
- 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. - Keep it secret. A key stays valid until it is revoked and has a daily budget of messages; past it the door answers
BUDGET_EXCEEDEDuntil the time inRetry-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.
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.
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.
curl -s https://ottonova-ai-assistant-dev.jovial-ottonova.de/a2a/orchestrator/.well-known/agent-card.jsonPublic, no key: the card names the endpoint, the skills and the personalApiKey scheme.
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.
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.
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.
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.
{
"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"
}
}| Code | HTTP status | When | Retry? |
|---|---|---|---|
UNAUTHENTICATED | 401 | No key, or a sign-in or service token instead of a key. | No |
KEY_NOT_FOUND | 404 | The key does not exist or was revoked. | No |
BUDGET_EXCEEDED | 429 | The key used up its daily budget; wait for Retry-After. | Yes |
UPSTREAM_UNAVAILABLE | 503 | A 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
- A2A specification — the protocol, its methods and the agent card.
- a2a-sdk for Python — the official client and server library.