Aletheia API Documentation
1. Overview
Aletheia is a grooming-detection API for conversational text. It scores a full conversation for predatory or harmful intent and, when two speakers are present, identifies which speaker is the predatory party. It runs on a hierarchical, age-aware transformer that models complete conversations rather than isolated messages, so it reads intent as it develops across a full exchange instead of flagging individual keywords.
The API returns a harmful-intent score, a human-readable risk level, a predicted predatory speaker with per-speaker probabilities, and a confidence estimate. Conversations are never retained. The service processes text in memory and returns a result.
Base URL: https://api.polycreek.org
All requests and responses are JSON over HTTPS. Plain HTTP requests are redirected to HTTPS.
2. Authentication
Aletheia uses bearer-token authentication. Include your API key in the Authorization header on every request to a protected endpoint:
Authorization: Bearer YOUR_API_KEY
Keys are issued by Polycreek. Store your key as a secret. Never embed it in client-side code or commit it to version control. If a key is exposed, contact us to rotate it.
Auth errors
| Status | Meaning |
|---|---|
401 Unauthorized | No API key was provided on a protected endpoint. |
403 Forbidden | The API key provided is invalid. |
The /health endpoint is public and does not require a key.
3. Endpoints
GET /health
Liveness check. No authentication required.
Response
{ "status": "healthy" }
status is "healthy" when the model is loaded and ready, or "degraded" if the service is up but the model is not yet available. Use this for uptime monitoring and readiness checks.
POST /predict
Score a single conversation. Requires authentication.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
conversation | string | yes | The conversation text, 1 to 500,000 characters. Include both speakers' turns. |
age_a | string | no | Age of speaker A. Accepts an exact age ("14"), a range ("13-17"), or a class ("minor", "adult"). |
age_b | string | no | Age of speaker B, same formats. |
Ages are optional. Provide whatever the platform already knows. Supplying ages activates the model's age-asymmetry pathway and improves accuracy, and omitting them runs a text-only prediction.
Example request
curl -X POST https://api.polycreek.org/predict \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"conversation": "user1: hey how was school today\nuser2: ok i guess\n...",
"age_a": "adult",
"age_b": "14"
}'
Example response
{
"conversation_preview": "user1: hey how was school today\nuser2: ok i guess...",
"harmful_score": 0.869,
"is_harmful": true,
"risk_level": "Critical",
"predatory_user": "user1",
"user_probabilities": {
"user1": 0.91,
"user2": 0.04,
"neither": 0.05
},
"confidence": 0.83,
"num_segments": 12,
"used_age_features": true
}
POST /batch_predict
Score up to 100 conversations in a single request. Requires authentication. More efficient than issuing many single /predict calls.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
conversations | array | yes | 1 to 100 items. Each item is either a plain conversation string, or an object with conversation and optional age_a / age_b (same formats as /predict). |
Example request
curl -X POST https://api.polycreek.org/batch_predict \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"conversations": [
"user1: hey\nuser2: hi",
{ "conversation": "user1: ...\nuser2: ...", "age_a": "adult", "age_b": "13" }
]
}'
Example response
{
"predictions": [
{ "harmful_score": 0.12, "is_harmful": false, "risk_level": "Safe", "...": "..." },
{ "harmful_score": 0.74, "is_harmful": true, "risk_level": "High Risk", "...": "..." }
]
}
Each item in predictions has the same shape as a /predict response, returned in the same order as the input.
4. Response Fields
Every prediction (from /predict or within a /batch_predict array) contains:
| Field | Type | Description |
|---|---|---|
conversation_preview | string | The first 500 characters of the submitted conversation, for reference. |
harmful_score | float | Probability of predatory or harmful intent, from 0.0 to 1.0. |
is_harmful | bool | true when harmful_score exceeds 0.5. |
risk_level | string | Human-readable band derived from the score (see below). |
predatory_user | string | Which speaker is predicted predatory: "user1", "user2", or "neither". |
user_probabilities | object | Probability distribution across user1, user2, neither (sums to about 1.0). |
confidence | float | 0.0 to 1.0 estimate of the model's certainty, combining harmful-score margin and speaker-attribution entropy. |
num_segments | int | Number of segments the conversation was split into (long conversations are chunked internally). |
used_age_features | bool | Whether speaker ages were supplied and used in this prediction. |
5. Risk Levels
risk_level maps the numeric harmful_score to a band for triage and display:
| Score range | Risk level |
|---|---|
0.00 to 0.19 | Safe |
0.20 to 0.39 | Low Risk |
0.40 to 0.59 | Medium Risk |
0.60 to 0.79 | High Risk |
0.80 to 1.00 | Critical |
The binary is_harmful flag flips at 0.50. For most moderation workflows we recommend routing on risk_level (for example, auto-queue High Risk and Critical, sample Medium Risk for review) rather than the binary flag alone, and tuning your action threshold against your own tolerance for false positives.
6. Providing Speaker Ages
Ages are optional but recommended when known. The model has a dedicated age-asymmetry pathway: telling it that one speaker is an adult and the other a minor sharpens its distinction between predatory adult-minor dynamics and benign peer interaction.
Accepted formats for age_a / age_b:
- Exact age.
"14","37" - Range.
"13-17" - Class.
"minor","adult"
If you supply no ages, the model runs text-only. Very short conversations with no age signal can score warm (mid-range) purely from register. Supplying ages, or evaluating longer exchanges, resolves most of this.
7. Rate Limits
The API permits up to 60 requests per 60-second window. Exceeding this returns:
| Status | Meaning |
|---|---|
429 Too Many Requests | Rate limit exceeded. Slow your request rate and retry. |
To score many conversations, prefer /batch_predict (up to 100 per call) over rapid single calls. If your pilot needs a higher sustained rate, contact us to raise your limit.
8. Errors
Errors are returned as JSON with an appropriate HTTP status and a detail message:
{ "detail": "API key required (Bearer token)" }
| Status | Meaning |
|---|---|
401 Unauthorized | Missing API key. |
403 Forbidden | Invalid API key. |
422 Unprocessable Entity | Request body failed validation (for example, missing conversation, empty string, over 500,000 chars, or more than 100 items in a batch). |
429 Too Many Requests | Rate limit exceeded. |
500 Internal Server Error | Prediction failed unexpectedly. |
503 Service Unavailable | The model is not currently loaded. Retry shortly. |
Client applications should handle 429 and 503 with retry and backoff, and treat 401, 403, and 422 as non-retryable configuration errors.
9. Data Handling & Privacy
Aletheia is built for child-safety use and handles data accordingly:
- No conversation retention. Submitted text is processed in memory to produce a score and is not stored by the service.
- Hash-based auditing only. When operational logging is enabled, the service records a score and a SHA-256 hash of the conversation, never the raw text, so a flagged case can be joined back on your side without Aletheia holding the content.
- Transport security. All traffic is served over HTTPS.
Because this API is intended to process conversations that may involve minors, integrators remain responsible for their own COPPA and data-handling obligations on the platform side. For deployments that cannot egress user chat at all, a single-tenant container that runs entirely inside your own environment is available. Contact Polycreek.
10. Quickstart
# 1. Check the service is up (no key needed)
curl https://api.polycreek.org/health
# 2. Score a conversation
curl -X POST https://api.polycreek.org/predict \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"conversation": "user1: ...\nuser2: ...",
"age_a": "adult",
"age_b": "14"
}'
Format conversations with one speaker turn per line (for example, user1: / user2: prefixes) so the model can attribute the predatory party. Read risk_level for triage, harmful_score for thresholding, and predatory_user to route the flagged party.
Need a key or a higher rate limit for a pilot? Get in touch.