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 business.trumis.com.au.
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
clip in the visitor's browser and sends it to the Trumis transcription
endpoint, where our AI provider (OpenAI) turns it into text with automatic
language detection — transcribed and discarded, never stored (disclosed at
/privacy/). 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. The visitor keeps a browser copy and Trumis retains the server-processed intake encrypted for up to 24 months (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. Each response returns
an intake_id; echo it on later turns so the encrypted archive keeps
every revision together. Records are retained for up to 24 months.
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. |
formal_requested | boolean | Reserved — the paid formal tier is currently disabled (see the changelog). Accepted for wire compatibility and ignored. |
payment_ref | string | Reserved while the paid formal tier is disabled — accepted and
ignored. When the tier is live: a Stripe Checkout session id from
/api/checkout, verified server-side with Stripe. No card
data touches Trumis; Stripe holds the payment record. |
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 so the conversational engine receives
the complete current context; the encrypted service archive also retains
each request and response revision. |
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 send-then-escalate path with response-day
clocks; receiver is the recognised organisation or
null. |
formal_offer | object | null | Currently always null — the paid formal tier is
disabled. When live: {text, fee}, shown with a written
account whose circumstances also clear the formal bar, presented on the
draft screen rather than in the chat flow. |
disclaimer | string | The legal-information disclaimer. Display it with the conversation. |
partner_tier | enum | Partner mode only: "licensed" or "pilot". |
The register (licensed)
Licensed partners can read a risk-tiered view of their own intake — the
governance register. A complaint produced through your embed or
partner-mode API carries a Reference: TRM-XXXX-XXXX line on
the document (also returned as complaint.reference), and the
register records the same category codes as the anonymous statistics plus
a deterministic risk tier (watch / elevated /
critical). The register stores no content of its own — its
schema (docs/register/ in the repository) has no free-text
columns, and each licence reads only its own rows.
GET /api/v1/register?days=90
X-Trumis-Key: <your licensed key>
→ { partner, window_days,
summary: { total, last_30_days,
tiers: {critical, elevated, watch},
lifecycle: {responded, resolved, overdue} },
entries: [ { reference, produced_on, kind, dispute_type, industry_code,
org_id, grounds_sufficient, urgency, risk_tier, amount_band,
evidence_direct, evidence_documentable, evidence_asserted,
channel, grounds: [argument_id…],
facts: [{fact_key, evidence_class}…],
events: {sent, response_received, deadline_passed, …},
edr_scheme } ] }
grounds are the KB argument codes that passed (what kind of
exposure); facts are the established circumstances (presence and
evidence class, never a value); edr_scheme is the external
dispute resolution body an unresolved complaint could reach. Errors follow
the standard catalog: 401 invalid_partner_key,
503 register_unavailable (store not enabled),
502 register_failed. Rate limit: 60/min per licence. A human
view lives at /register/.
Lifecycle events. The status column is driven by pings the complainant's device sends as it tracks the response clock:
POST /api/v1/register-event
{ "reference": "TRM-XXXX-XXXX",
"event": "sent" | "response_received" | "deadline_passed"
| "escalated" | "resolved" | "withdrawn" }
→ 204
Day-granular, idempotent per (reference, event), content-free. The
endpoint reveals nothing about which references exist; a malformed shape is
400 invalid_event.
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 | The claims card is live. | Render confirm.claims as pre-ticked options. |
account_ready | The written account is done. | Show the account with its send path. (While the paid tier is
disabled, formal_offer stays null here.) |
grounds_ready | The formal complaint is built. | Show complaint.text and the routing path. |
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; the person still controls sending, and that approval is the send. 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.