Trumis / Developers
Integrate Trumis
Two integration paths, both live today: a one-line embeddable widget, and a retained-record JSON API. This page is the full contract. The buyer's view — pricing model, security posture, record-keeping — lives at trumis.com.au/organisations.
Embed · API · Request · Response · Stages · Errors · Rate limits · Partner tiers · Receiving complaints · Versioning · OpenAPI · Changelog
Option 1 — the one-line embed
Paste this where your complaint form lives. Works today, no registration — that's the evaluation (pilot) tier:
<script src="https://trumis.com.au/embed.js"
data-partner="Acme Energy"
data-email="complaints@acme-energy.example"
data-height="640"></script>
data-partner— your organisation's name; the complaint is addressed to you and framed under your complaint-handling policy.data-email— optional; pre-addresses the send button to your complaints inbox. The widget always shows people where their complaint will go — quiet misdirection isn't possible.data-height— iframe height in pixels, default 640.data-lang— optional opening language hint (zh,vi,ar,el).- Microphone: the widget's Talk button records a short audio clip
(transcribed by our provider, then discarded), so the iframe ships with
allow="microphone". If your site sends a restrictivePermissions-Policyheader, includemicrophone=(self "https://trumis.com.au")or the Talk button quietly disappears — everything else works regardless.
Pilot embeds carry a small “Powered by Trumis” mark. Licensed embeds swap the page-supplied attributes for a registered partner id:
<script src="https://trumis.com.au/embed.js"
data-partner-id="acme-energy"
data-height="640"></script>
With data-partner-id, the organisation name, complaints inbox
and branding (including watermark removal) resolve server-side from your
licence configuration via /api/embed-config, and the widget
verifies it is running on one of your registered origins. Copying the id onto
another site fails that check and falls back to the watermarked pilot
behaviour — branding cannot be spoofed or stripped from page HTML. Licensing:
info@trumis.com.au.
The embed is a sandboxed iframe served from trumis.com.au. It adds no scripts to your page context, and conversations stay in the visitor's browser — no conversation content is stored on Trumis servers (security, privacy).
Option 2 — the retained-record API
One endpoint drives the whole conversation. Send the visible transcript each turn plus the facts returned last turn; render the reply. When the person confirms, the response carries the finished complaint. The service holds no state between calls, so there is nothing to provision and nothing to delete.
curl -X POST https://trumis.com.au/api/v1/companion \
-H 'Content-Type: application/json' \
-d '{
"history": [{"role":"user","content":"They cut my power off with no warning."}],
"known_facts": {},
"client_done": false,
"partner": { "name": "Acme Energy" }
}'
JavaScript, each turn feeding the previous response back:
const r = await fetch('https://trumis.com.au/api/v1/companion', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }, // add 'X-Trumis-Key' when licensed
body: JSON.stringify({
history: transcript, // [{role:'user'|'assistant', content}]
known_facts: lastTurn.facts, // echo the previous turn's facts verbatim
client_done: false,
partner: { name: 'Acme Energy' },
}),
});
const turn = await r.json();
transcript.push({ role: 'assistant', content: turn.reply });
Python:
import requests
turn = requests.post(
"https://trumis.com.au/api/v1/companion",
json={"history": transcript, "known_facts": last_facts, "client_done": False,
"partner": {"name": "Acme Energy"}},
timeout=30,
).json()
CORS is open and requests carry no cookies, so the API can be called directly from your own complaint page's front end. Responses are synchronous JSON (no streaming); a turn typically returns in one to three seconds — the deterministic rules run first and the AI layer only phrases conversation.
Request fields
| Field | Type | Notes |
|---|---|---|
history | array, required | {role: "user"|"assistant", content: string} — the whole
visible transcript, oldest first. Capped at 60 turns × 8,000 characters;
longer input is truncated, not rejected. |
known_facts | object | The facts object from the previous response, echoed back
verbatim. Setting a fact to false retracts it — that is how
unticking a confirm claim works. |
client_done | boolean | true when the person indicates they have nothing more to
add (your “I'm done” control). |
partner | object | {name: string} switches to organisation intake: the
complaint is lodged with you under your policy, no third-party routing.
With a licensed X-Trumis-Key header the name is taken from
your registry entry, not the body. See partner
tiers. |
evidence | object | A correspondence transcript from /api/v1/evidence,
after the person has reviewed it:
{source, transcript: [{speaker, name?, time?, text}]}. |
lang | string | Opening-language hint (zh, vi,
ar, el). The person's own words always win. |
Response fields
| Field | Type | Notes |
|---|---|---|
reply | string | The agent's next message, in the person's language. Render it and
append it to history. |
stage | enum | Where the conversation is — see stages. |
method | enum | "rules" or "rules+llm" — whether the AI
layer was active for this turn. Merit decisions are deterministic either
way. |
facts | object | Everything established so far. Echo it back as
known_facts next turn — the encrypted intake archive holds
nothing. |
grounds | object | {sufficient: boolean, passed_arguments: [{id, summary}]}
— the rules engine's merit decision, with the arguments that passed.
Auditable: these ids map to the maintained legal knowledge base. |
urgency | array | [{level: "critical"|"high"|"medium", message, action}] —
route critical flags (e.g. safety) to emergency services
messaging before anything else. |
confirm | object | null | Present at stage confirming: the exact claims the
complaint would make, as tickable lines with the person's quotes —
{intro, claims: [{key, label, quote?, theirs?}], about, subject?,
question, ui: {tick_prompt, unsure_note, confirm_yes, you_said,
their_words}}. Untick = echo that fact back false;
confirm = send the ui.confirm_yes message. The card strings
arrive in the person's language so thin clients stay thin. |
complaint | object | null | The finished complaint once the person confirms:
{document, text, routing, receiver}. text is
the complete complaint ready to send; document is the same
content structured (title, sections, outcomes, evidence list);
routing is the two-step send path with response-day clocks
— the organisation, then that sector’s free external body;
receiver is the recognised organisation or
null. |
complaint.document.sections | array | Includes a context section (“COMPLAINT-HANDLING
CONTEXT”) when the merit gate opened: the complaint-handling
framework for the recognised organisation’s sector, written for
whoever routes the complaint. It states what the framework requires; it
makes no allegation. Absent in partner mode, which has its own policy
framing. |
referral | object | null | {text, url} — where to take it further, present with a
written account whose circumstances also clear the formal bar. Trumis
does not draft complaints for lodgement with an ombudsman; this points
to FairClaim, which does. Show it on your draft screen, not in the chat
flow. |
disclaimer | string | The legal-information disclaimer. Display it with the conversation. |
partner_tier | enum | Partner mode only: "licensed" or "pilot". |
Stages
| Stage | Meaning | Client behaviour |
|---|---|---|
listening | Free talk; the person is telling their story. | Render the reply; keep the composer open. |
probing | One gentle question is being asked. | Same as listening. |
confirming | Partner intake only: the claims card is live. | Render confirm.claims as pre-ticked options. |
account_ready | The written account is done (with
referral when the formal bar is also cleared). |
Show the account; show the referral on the draft screen. |
grounds_ready | Partner intake only: the document lodged with the organisation is built. | Show complaint.text and the routing path. |
safety | The person disclosed thoughts of suicide or self-harm. The reply carries the crisis lines and is the whole turn. | Render the reply and the critical urgency banner; show no
draft, no referral, no next step. |
no_grounds_close | The facts don't support a formal complaint; the close is warm and explains why. | Render the reply; no document exists to show. |
Errors
Every error is JSON with a machine code and a human message, and every
response — success or error — carries an X-Trumis-Request-Id
header. Quote that id when writing to support.
| Status | error | When |
|---|---|---|
| 400 | invalid_json | The body is not valid JSON. |
| 400 | no_screenshots | /evidence without OCR lines. |
| 401 | invalid_partner_key | X-Trumis-Key doesn't match a configured partner. |
| 404 | unknown_partner | /embed-config pid not registered. |
| 405 | method_not_allowed | Wrong HTTP method; Allow header lists the right ones. |
| 422 | nothing_recognised | /evidence OCR lines yielded no correspondence. |
| 429 | rate_limited | Budget spent — honour Retry-After. |
| 502 | companion_failed | Upstream hiccup; safe to retry. Body includes request_id. |
Rate limits
Fair-use limits protect availability. Current budgets, per rolling minute:
| Caller | Budget | Keyed by |
|---|---|---|
| Unauthenticated (consumers, pilots) | 30 requests/min | Client address |
| Licensed partner key | 120 requests/min | The key's partner id |
Every response carries X-RateLimit-Limit,
X-RateLimit-Remaining and X-RateLimit-Reset (seconds).
A conversation turn is one request, so 30/min is generous for a person typing;
server-side integrations that multiplex many users through one address should
use a licensed key. Higher budgets are a licence property — ask
info@trumis.com.au.
Partner tiers and keys
| Pilot (evaluation) | Licensed | |
|---|---|---|
| How | partner.name in the body / embed attributes |
X-Trumis-Key header / data-partner-id embed |
| Organisation name on documents | As supplied, marked pilot | From the registry — server-authoritative |
| “Powered by Trumis” mark | Always shown | Removable |
| Embed origin verification | — | Registry allow-list |
| Rate budget | 30/min per address | 120/min per key (raisable) |
| Cost | Free | Licence |
Keys are secrets for server-side use — never ship one in page JavaScript.
Embeds don't need keys: the public data-partner-id plus the
origin check does the equivalent job in the browser.
Receiving complaints into your systems
Trumis retains an encrypted complete intake record for up to 24 months, while the person still controls whether the complaint is sent. You have two ways to receive:
- Embed: the finished complaint arrives at your complaints inbox as an email from the person's own account — plain text, structured sections, with the correspondence annex. Parse-friendly and, importantly, the timestamps and sender are the complainant's own record too.
- API integration: your page or backend drives the
conversation, so the
complaintobject — document, structured sections, routing, receiver — is already in your hands the moment the person confirms. Post it into your case system directly; there is no Trumis-side webhook because the complaint is never on our side to push.
Record-keeping guidance for regulated intake (RG 271 and ombudsman schemes) is on the organisations page.
Versioning and deprecation
/api/v1/…is the canonical path. The unversioned/api/…paths are permanent aliases of v1.- Additive changes (new response fields, new optional request
fields, new enum values on
urgency) ship without a version bump — build clients that ignore unknown fields. - Breaking changes (removing or renaming fields, changing
semantics) ship as
/api/v2/…. v1 then runs for at least 12 months, and licensed partners are notified by email before anything is scheduled for shutdown. - Every observable change is dated in the changelog. The machine-readable contract is /openapi.json.
Evidence endpoint
POST /api/v1/evidence — screenshots in, correspondence out.
Clients run OCR on the device (tesseract.js on the web, iOS Vision,
Android ML Kit) and send only text lines with geometry — images never leave
the person's device. The endpoint reconstructs the conversation
deterministically (who said what, when — deduplicated and stitched across
scrolling captures) and returns it with proposed facts for the person to
review. Limits: 25 screenshots, 500 lines each. Full schema:
/openapi.json.
Health
GET /api/v1/health → {ok, service, time, region} —
liveness for your monitors, no AI call involved. Live state:
trumis.com.au/status.