Run an assessment via the API
Start an assessment for one of your members from your own backend and run the conversation in your own interface.
Use this API to run the conversation in your own interface (a native mobile app, a desktop tool, a backend job, or any surface where you render the conversation yourself and own every turn). Claire, the AI agent, still asks the questions and scores the evidence; you carry the messages.
Prefer to let Wiselook render the conversation on your own website instead? Then embed the widget: Wiselook handles the streaming and the session, and your backend only mints visitor sessions.
Consent is yours to obtain. You are the data controller for your members. Starting an assessment notifies nobody, so make sure the person has agreed before you begin.
Prerequisites
- An API client with the
evaluations:writepermission, plusevaluations:readto poll the result. - A bearer token minted from that client, as described in
Authentication. The examples
below assume it is in
$TOKEN. - A member to assess. This flow assesses people who are already on your Members list, and never creates them. To add someone first, see Bulk-invite members.
- One of your organisation's adopted methodologies.
1. Find the member
Every assessment names the person it is for. Use the member's id, as
returned by the Members API:
curl "https://$WISELOOK_HOST/v1/tenant-users?email=maria@example.com" \
-H "Authorization: Bearer $TOKEN"
Reading members needs the tenant_users:read permission. See
Manage members via the API.
2. Start the assessment
curl -X POST "https://$WISELOOK_HOST/v1/members/$TENANT_USER_ID/evaluations" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"methodology_id": "<ADOPTED_METHODOLOGY_UUID>"}'
{
"id": "7c1e9a04-…",
"tenant_user_id": "3f8b2d61-…",
"methodology_id": "009f91ce-…",
"status": "in_progress",
"locale": "en-US",
"channel": "text",
"created_at": "2026-09-18T09:14:22Z"
}
Keep the id. It is the evaluation_id every later call uses. The
row echoes the member back as tenant_user_id: the same id you passed
in the path, named for the field it fills on the assessment.
| Field | Required | Notes |
|---|---|---|
methodology_id | yes | Must be one your organisation has adopted |
locale | no | Defaults to the member's preferred language, then the methodology's default |
channel | no | text (default) or voice. Fixed once the assessment starts |
Locale and channel cannot be changed afterwards.
Common refusals:
| Status | Meaning |
|---|---|
400 | The person is not an assessable member of your organisation. Removed members and unknown ids answer the same way |
403 | The token lacks evaluations:write |
409 | Your assessment balance is exhausted |
422 | The methodology is not adopted by your organisation, or a field is invalid |
3. Run the conversation
Send each of the person's turns to the chat endpoint. The reply streams back as server-sent events:
curl -N -X POST \
"https://$WISELOOK_HOST/v1/members/$TENANT_USER_ID/evaluations/$EVALUATION_ID/chat/messages" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"content": "Hello"}'
What comes back
The response is text/event-stream. Each event is an SSE event: line
and a data: line of JSON. The JSON repeats the event name in a type
field, so you can switch on either one:
event: message_start
data: {"type": "message_start", "message_id": "m_resp_7c1e9a04_1", "role": "assistant"}
event: message_delta
data: {"type": "message_delta", "message_id": "m_resp_7c1e9a04_1", "content_delta": "Got "}
event: message_delta
data: {"type": "message_delta", "message_id": "m_resp_7c1e9a04_1", "content_delta": "it. "}
event: message_delta
data: {"type": "message_delta", "message_id": "m_resp_7c1e9a04_1", "content_delta": "Tell me about a deadline you missed. "}
event: message_complete
data: {"type": "message_complete", "message_id": "m_resp_7c1e9a04_1", "content": "Got it. Tell me about a deadline you missed. "}
event: progress_update
data: {"type": "progress_update", "current_eco_id": "3f2a7b18-…", "progress_percent": 25}
event: stream_end
data: {"type": "stream_end", "reason": "complete"}
These are semantic events, never raw model tokens.
| Event | Fields | What to do with it |
|---|---|---|
message_start | message_id, role | A reply is beginning |
message_delta | message_id, content_delta | Append the fragment to what you are showing |
message_complete | message_id, content | The whole reply, assembled. Use it to verify what you rendered |
progress_update | progress_percent, current_eco_id | Update any progress indicator |
assessment_complete | evaluation_id, status | The conversation is over. Stop sending turns |
stream_end | reason | This turn's stream is closed |
Every turn ends with stream_end. On the final turn, and only then,
assessment_complete arrives just before it.
Handling the loop
One request per turn. Read the stream to its end, show the reply, collect
the person's answer, then send the next turn. Keep going until you see
assessment_complete.
import json, requests
BASE = f"https://{WISELOOK_HOST}/v1/members/{tenant_user_id}/evaluations/{evaluation_id}"
HEADERS = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
def send_turn(text: str) -> tuple[str, bool]:
"""Send one turn. Returns the assistant's reply and whether we are done."""
reply, done = "", False
with requests.post(
f"{BASE}/chat/messages", headers=HEADERS, json={"content": text}, stream=True
) as r:
r.raise_for_status()
for line in r.iter_lines(decode_unicode=True):
if not line or not line.startswith("data: "):
continue # the `event:` line repeats what `type` already tells us
event = json.loads(line[len("data: "):])
if event["type"] == "message_delta":
reply += event["content_delta"]
print(event["content_delta"], end="", flush=True)
elif event["type"] == "assessment_complete":
done = True
elif event["type"] == "error":
raise RuntimeError(event["message"])
return reply, done
done = False
answer = "Hello"
while not done:
_, done = send_turn(answer)
if not done:
answer = input("\n> ")
Three things this example is careful about, because they are easy to get wrong:
- Read to the end of the stream before sending the next turn. A turn is finished when its stream closes, not when the text stops arriving.
- An error arrives inside a healthy stream. The HTTP status is already
200by the time events flow, so a failure mid-turn comes as anerrorevent followed bystream_endwithreasonset toerror. Handle the event; do not rely on the status code alone. Theerrorpayload carriescode,messageandretryable, so you can tell a transient failure from one worth surfacing. - Accumulate the deltas yourself.
message_completecarries the full text, so you can use it as a check on what you assembled, or simply render it if you do not want to stream.
The conversation endpoint is nested under the evaluation on purpose. The assessment it belongs to is part of the address, so a turn can never be sent to somebody else's conversation by mistake.
4. Read the result
After assessment_complete, scoring runs in the background. The
evaluation moves to completed when the report is ready. Poll it:
curl "https://$WISELOOK_HOST/v1/members/$TENANT_USER_ID/evaluations/$EVALUATION_ID" \
-H "Authorization: Bearer $TOKEN"
Reading needs the evaluations:read permission, separate from the
evaluations:write that starts an assessment. A client that only polls
results should not also be able to start them. Grant both to one client
if it does both.
Scores and report fields stay null until the status is completed.
What you can and cannot do
- You cannot assess a removed member. Invited members, who have not signed in yet, are allowed.
- An evaluation belongs to the organisation that created it. A token from another organisation cannot read it or send turns to it.
- Members here, visitors in the widget. A member is on your Members list with a role, a status and tags. A widget visitor is identified by whatever string your backend passes when it mints the session, and is a separate record even when that string names someone who is also a member. Pick the route that matches who you are assessing.
See also
- Bulk-invite members to add the people you want to assess.
- Manage members via the API to find their ids.
- API clients for creating the client and choosing its permissions.
- Embed the widget to let Wiselook host the conversation on your site instead.