Skip to main content

Trust platform API

Integrations

Search → generate → poll — directory resolution, context-scoped trust reports, and webhooks on the same JSON contract your hub uses.

Server-to-server integration for integrators with a verified TumiTrust account (organization or individual, depending on how you onboard): authenticate, resolve a person in directory, start a context-aware trust report, read structured JSON you can explain to a committee, and optionally receive webhooks instead of polling.

Golden path (about ten minutes)

Create a sandbox key under API & trust signals in your TumiTrust workspace, export it as TUMITRUST_API_KEY, then run ping → directory search → profile row → start trust lookup (generate) → poll report JSON—the same trust lookup organizations run from the hub, over HTTP. Poll the report URL immediately after generate—the read path is engineered to be fast, and many runs return a complete report on the first GET. Use a sandbox key for integration and testing; switch to a live key only after account verification and a deliberate go-live—sandbox and live API keys ↓. Every hop below is spelled out in cURL through C++, Go, and Rust. Add webhooks when your HTTPS endpoint is ready—register, test, list ↓.

Self-service access

Register on tumitrust.com, then create sandbox and live API keys under API & trust signals in your workspace. Complete account verification in Settings to enable live keys for production traffic.


PTI-ID subject references (partner integrations)

Partner-provisioned entities use PTI-ID as the canonical subject key:

FieldFormatExample
pti_idpti_ + Crockford Base32pti_9A72X8K2

Use pti_id in partner widget URLs, webhook payloads, and trust signal subject references when the entity was onboarded through a data partner. Map your platform's merchant or subscriber ID to PTI-ID during partner onboarding.

Native TumiTrust members continue to use numeric subject_user_id for directory and report flows.


Who this guide is for

AudienceUse this page to…
Technical implementersImplement the HTTP contract, retries, and parsers your services will run in sandbox and production.
Security & platform teamsReview where secrets live (server only), which routes use API key vs Bearer JWT, and how webhook signatures are verified.
Product / ops at a lender, employer, landlord, or programUnderstand the same flows the TumiTrust console covers under API & trust signals—so walkthroughs, audits, and vendor scopes stay aligned.

If you only use TumiTrust as an individual on the platform, you do not need this guide—start with For individuals and Get started.


Why teams pick TumiTrust reports

Not just a score

A trust report you can aim at the decision you are actually making—then defend in plain language when someone asks “why?”

Contexts = your lens

You choose one or more life slices—for example lending, rental, or employment—so the report reflects the risk story that matters for that product, not a generic blob.

Explainability in that slice

The returned report JSON carries structured rationale for the contexts you asked for (as deep as your plan unlocks)—so credit, HR, housing, or program teams see drivers and narrative alongside the headline signal, not a mystery number.

Integrate, meet workflow

Same HTTP contract powers a crisp underwriting UX, a fair-housing review, or a batch file—fast reads, optional webhooks, and a payload your auditors can trace back to the slice you scoped.

Valid contexts[] values depend on your plan; the API rejects unknown slugs so you never silently get the wrong slice. Copy strings from the canonical ID list below. When you are ready to wire it, jump to From zero to first report and the contexts callouts around Start a report.


Trust report context IDs (documented)

These snake_case strings are the documented contexts[] values for POST …/trust-reports/reports/generate/. Your plan may allow a subset; anything else returns 400. Use the first column verbatim in JSON (underscores matter—e.g. informal_sector, digital_platform, faith_mutual_aid, risk_compliance).

#String for contexts[]Primary / lensHuman label
1lendingPrimaryLending & Borrowing
2rentalPrimaryRental & Housing
3employmentPrimaryEmployment & Work
4insurancePrimaryInsurance & Risk
5merchantPrimaryMerchant & Business
6partnershipPrimaryBusiness Partnerships
7supplierPrimarySupply Chain
8informal_sectorPrimaryInformal Economy
9agriculturalPrimaryAgriculture & Farming
10remittancePrimaryMoney Transfer
11educationLensEducation & Skills
12healthLensHealth & Care
13familyLensFamily & Household
14civicLensCivic & Public Life
15digital_platformLensDigital & Platform Work
16mobilityLensMobility & Logistics
17utilitiesLensUtilities & Subscriptions
18creativeLensCreative & Gigs
19faith_mutual_aidLensFaith & Mutual Aid
20risk_complianceLensRisk & Compliance

Primary = core life-area scores. Lens = cross-cutting views derived from primary signals (same JSON field, different slice). For stories and examples—not API IDs—see Trust contexts.


What you are integrating (five moves)

  1. Authenticate every call with X-API-Key (from API & trust signals in your workspace).
  2. Resolve the subject with directory search (add phone, email, national ID, country—never rely on name alone in production).
  3. Confirm the row with GET …/directory/people/<id>/ when a human or policy needs certainty.
  4. Start a report with POST …/trust-reports/reports/generate/ (non-empty contexts[] your plan allows—each ID is a lens on trust, not decoration).
  5. Consume GET …/trust-reports/reports/<report_id>/ until the payload is complete; treat the report object as your integration contract—scores plus slice-specific explainability—and ignore unknown keys.

Origin: https://tumitrust.com — JSON lives under /api/v1/ on that host. Rule of thumb: product JSON for directory, reports, workspace, billing exports, trust signals, and webhooks → /api/v1/.

Polling and webhooks

Report generation is asynchronous. After POST …/generate/, poll GET …/reports/<report_id>/ until report.status is completed or failed. Many reports finish on the first poll; if not, retry every 1–2 seconds with exponential backoff up to a sensible client timeout.

For server-to-server integrations, prefer webhooks (configured in your workspace) so your system is notified when a report is ready instead of polling continuously.


Your API key: create, store, rotate

  1. Create — In API & trust signals, create a named key. Sandbox keys (TT_SANDBOX-…) are available as soon as you can open that page. Live keys (TT_LIVE-…) appear only after TumiTrust has approved your account verification—until then, the console explains that live keys are not available yet (and automation receives a clear “verification required” response instead of a key). Copy the full secret once; you cannot see it again later.
  2. Store — Password vault or secure server config only. Never email it, never paste into public chat, never ship it inside a mobile app binary.
  3. SendServer-side only, with the headers below.
  4. Rotate — Revoke in the hub and issue a new key if exposure is possible; roll config before deleting the old credential.
  5. Revoke — Stops that string immediately; it does not erase billing history, balances, or welcome report credits tied to the account.

Headers (machine-to-machine):

X-API-Key: <your TUMITRUST API key>
Accept: application/json

Below, TUMITRUST_API_KEY is that secret. TUMITRUST_BASE_URL defaults to https://tumitrust.com/api/v1 in the language examples.


Sandbox and live API keys

Sandbox and live API keys

Same HTTP contract—different stakes and guardrails. Use a sandbox key while you build and test; promote to live only after verification clears your account for production traffic.

Sandbox

TT_SANDBOX-…

Sandbox traffic — integrate clients, validate parsers, and exercise webhooks before production. Search and read endpoints remain available for development.

Daily generate cap (UTC): In sandbox, only successful “start a new trust report” calls share one daily allowance for your account— 10,000 on our default hosting, counted in UTC; every sandbox key uses the same tally. Search, reads, and everyday API traffic are not part of this cap. Successful responses —and your API & trust signals / keys view—show how many starts remain and when the count resets. If you reach the cap, wait until UTC midnight or use a live key for steady production volume.

Live

TT_LIVE-…

Production traffic — real applicants, real decisions. No sandbox daily generate cap: volume follows plan, credits, and contract.

Appears only after account verification; keep the secret in production-grade stores only.

Welcome report credits are per TumiTrust account, not per key.

Sandbox (TT_SANDBOX-)Live (TT_LIVE-)
Who can createAnyone on your team who can open API & trust signalsAfter account verification is approved
Where it belongsLaptops, staging, training screenshotsProduction secret store only

Quick reference

StepGET / POSTAuth
HealthGET …/ping/X-API-Key
Find someoneGET …/directory/people/search/?…X-API-Key
Confirm rowGET …/directory/people/<id>/X-API-Key
Start reportPOST …/trust-reports/reports/generate/X-API-Key + JSON body
Read resultGET …/trust-reports/reports/<report_id>/X-API-Key
Deeper subject JSON (optional)GET …/trust-signals/subjects/<numeric_user_id>/Authorization: Bearer <JWT>
Webhook management…/trust-signals/webhooks/…Authorization: Bearer <JWT>

Sandbox “start report” budget (UTC): only successful POST …/trust-reports/reports/generate/ responses count. Hosted accounts include 10,000 successful generations per UTC calendar day (every sandbox key on the account shares one counter). See sandbox_daily_successful_report_generates on each success, policy.sandbox_daily_successful_report_generates_limit on GET /api/v1/auth/api-keys/, and sandbox and live API keys.


Institution report lookup (trust-reports API)

Primary integration guide

Search → generate → poll — the contract your Lookup Studio and automation share

Using the institution hub? See Lookup Studio (dashboard) below — same steps, no separate API host. Automating? Copy the examples on this page; each requested trust context appears as its own object in trust_intelligence.context_intelligence[].

Audience: institution trust consumers — banks, landlords, employers, insurers — integrating the same search → generate → poll → download flow the Lookup Studio runs in the hub.

All paths below live under https://tumitrust.com/api/v1/trust-reports/.

Lookup Studio (hub dashboard)

If your team runs lookups in the institution hub (not curl), you are still exercising the same product — the console calls the routes below on your behalf.

Step in Lookup StudioWhat you doDocs / API equivalent
1. Find subjectSearch by name, phone, or email in Lookup StudioDirectory search (preferred) or shortcut search
2. Choose contextsPick one or more life-area chips (e.g. lending + merchant)Same IDs as Trust report context IDs — passed as contexts[]
3. GenerateClick Generate report (uses report credits)POST …/trust-reports/reports/generate/
4. ReviewOpen the report row; Insights Studio charts loadPoll GET …/reports/<report_id>/ — read report.trust_intelligence
5. DownloadPDF button when readyGET …/reports/<report_id>/download/

Where to start in the hub: /institution/<your-slug>/Lookup Studio (or Trust Intelligence for pipeline stats). For onboarding and hub navigation, see For institutions.

Hub vs API

The hub is the guided UI; this page is the machine contract. When your policy team asks “what JSON do we get?”, use the completed report example below — one context_intelligence block per context you selected, never merged into a single blob.

Authentication

X-API-Key: TT_SANDBOX-…   # sandbox
X-API-Key: TT_LIVE-… # production (after verification)
Accept: application/json

Keys are created under API & trust signals in your workspace. See Your API key and sandbox and live API keys.

Endpoints

StepMethodPathPurpose
Shortcut searchGET/search/?q=<query>Institution-scoped name/email search (min 2 chars)
GeneratePOST/reports/generate/Start async report generation
PollGET/reports/<report_id>/Status + full report JSON when ready
IntelligenceGET/reports/<report_id>/intelligence/trust_intelligence.v1 explainability only
Download PDFGET/reports/<report_id>/download/PDF attachment (when generated)
History statsGET/reports/stats/Aggregate counts for your institution
ListGET/reports/Paginated report history

Prefer directory search for production identity resolution: GET /api/v1/directory/people/search/ supports phone, email, national ID, and country filters — see From zero to first report. Use GET …/trust-reports/search/ only as a shortcut when you already have a relationship-scoped name/email hint.

Typical flow (copy-paste)

1. Search (optional shortcut)

GET /api/v1/trust-reports/search/?q=Ada
X-API-Key: TT_SANDBOX-…
Accept: application/json

Expected response (200):

{
"success": true,
"results": [
{
"id": "42",
"name": "Ada Mwansa",
"email": "ada@example.com",
"phone": "+260971234567",
"has_recent_report": false
}
]
}

Use results[].id as subject_user_id in generate. For richer directory hits (PTI-ID, partner-represented subjects), use GET /api/v1/directory/people/search/ instead.

2. Generate

POST /api/v1/trust-reports/reports/generate/
Content-Type: application/json
X-API-Key: TT_SANDBOX-…

{
"subject_user_id": "42",
"report_tier": "detailed",
"contexts": ["lending", "merchant"],
"notify_ready_email": false
}
FieldRequiredValues
subject_user_idYes*Numeric user id from search
pti_id / partner_entity_idYes*Partner-provisioned subjects (see PTI-ID)
report_tierNobasic, detailed, or predictive (default basic). External screening is not a report_tier — use external screening API when the subject is not in directory.
contextsNoNon-empty list from Trust report context IDs your plan allows
lookup_profileNoWorkflow preset when contexts is omitted: risk_assessment, merchant_onboarding, lending, or general_trust. When directory search fails, use screening API with the same lookup_profile.
notify_ready_emailNoDefaults false for API keys; true for session/JWT unless set

Expected response (200 — processing):

{
"success": true,
"report_id": "52316c19-adfe-49df-a29a-9d918c86dc12",
"status": "processing",
"credits_used": 1,
"credits_remaining": 47
}

Save report_id — it is the only ID that belongs in poll and download URLs.

Error outcomes you should handle:

HTTPMeaning
400Invalid or unknown contexts[], missing subject
402Insufficient report credits
429Sandbox daily generate cap reached (UTC) — see sandbox and live API keys

2b. External screening (subject not in directory)

Use this when directory search returns no match but you have strong identifiers. You do not pass report_tier: screening_dossier — that tier is applied automatically by routing.

Hub parity: the Lookup Studio does this for you when search returns zero results after you pick Risk assessment (or any workflow). Integrators call the screening endpoint directly.

POST /api/v1/trust-intelligence/screening/
Content-Type: application/json
X-API-Key: TT_SANDBOX-…

{
"display_name": "Amara Phiri",
"lookup_profile": "risk_assessment",
"subject_identifiers": {
"national_id": "112233/44",
"phone": "+260971234567",
"country": "ZM"
}
}
FieldRequiredNotes
display_nameYesLegal or display name for the screening record
subject_identifiersYes*At least one of national_id, passport, phone, or email
lookup_profileNoDefault risk_assessment — sets screening pack and contexts (same presets as full reports)
partner_entity_idNoYour platform’s reference id for the subject

First touch (subject not on TumiTrust yet) — 202:

{
"success": true,
"lookup_mode": "screening_dossier",
"profile_kind": "screening_provisional",
"is_provisional": true,
"pti_id": "pti_…",
"intel_case_ref": "IC-…",
"screening": { "screening_summary": { "overall_status": "clear" } },
"compliance_intelligence": { }
}

Repeat lookup (same identifiers, subject now on record) — 200:

{
"success": true,
"lookup_mode": "full_report",
"pti_id": "pti_…",
"screening": { }
}

After a provisional record exists, use pti_id (or directory search) with POST …/trust-reports/reports/generate/ for a full basic, detailed, or predictive report.

List screened subjects awaiting claim:

GET /api/v1/trust-intelligence/screening/
X-API-Key: TT_SANDBOX-…

Returns registry[] with pti_id, display_name, screening_count, and claim_status.

Credits: external screening consumes external screening report credits (operator-configurable, separate from basic/detailed/predictive). See Risk & Compliance Intelligence — Screening.

3. Poll until complete

GET /api/v1/trust-reports/reports/52316c19-adfe-49df-a29a-9d918c86dc12/
X-API-Key: TT_SANDBOX-…

Poll report.status inside the report object — not a top-level status field on the envelope:

# jq one-liner after each GET
jq -r '.report.status // empty'

While processing: report.status may be processing or pending. Done: completed. Failure: failed.

How to read the finished payload

  • report.contexts_included — the context slugs you requested (e.g. lending, merchant).
  • report.trust_intelligence.context_intelligenceone entry per context, in the same order as your request. Each entry has its own score, band, and explainability for that life slice.
  • report.trust_intelligence.headline — 0–1000 summary across the scoped lookup (not a substitute for per-context blocks).
  • report.trust_intelligence.compliance_intelligence — additive risk & compliance envelope (compliance_intelligence.v1): screening summary, coverage gaps, confidence, and compliance lens. Mobile clients may ignore until they opt in via schema_version.
Expected response (200 — completed) — full illustrative JSON
{
"success": true,
"report": {
"id": "52316c19-adfe-49df-a29a-9d918c86dc12",
"user": {
"id": "42",
"name": "Ada M.",
"email": "ada@example.com",
"phone": "+260971234567"
},
"ubuntu_score": 620,
"risk_rating": "moderate",
"status": "completed",
"report_type": "detailed_report",
"contexts_included": ["lending", "merchant"],
"verification_report_id": "TUMITRUST-128-1781967718",
"score_contract": { "version": "2026.1" },
"trust_assurance": {
"artifact": "institution_credit_report",
"report_tier": "detailed",
"contexts_included_count": 2,
"methodology_note": "Scores follow the active TumiTrust score contract."
},
"trust_intelligence": {
"schema_version": "trust_intelligence.v1",
"status": "completed",
"headline": {
"tumitrust_score": 620,
"score_scale": "1000",
"score_max": 1000,
"score_band": "Watch",
"narrative": "Headline trust 620 / 1000 (Watch) for lookup scoped to Lending & Borrowing and Merchant & Business."
},
"lookup_scope": {
"contexts_included": ["lending", "merchant"],
"contexts_count": 2,
"report_tier": "detailed"
},
"context_intelligence": [
{
"context_id": "lending",
"label": "Lending & Borrowing",
"score": 580,
"score_scale": "850",
"score_max": 850,
"score_band": "Developing",
"selected_for_report": true,
"data_sources": ["platform_activity", "endorsements"],
"explain_summary": {
"summary": "Repayment history and income stability support this lending slice.",
"factors": [
{
"label": "On-time repayment share",
"weight_pct": 41,
"direction": "positive",
"source_type": "platform_activity"
},
{
"label": "Community validation depth",
"weight_pct": 22,
"direction": "positive",
"source_type": "endorsement"
}
]
}
},
{
"context_id": "merchant",
"label": "Merchant & Business",
"score": 612,
"score_scale": "850",
"score_max": 850,
"score_band": "Established",
"selected_for_report": true,
"data_sources": ["commerce_partner", "pti_signals"],
"explain_summary": {
"summary": "Order fulfillment consistency and a low dispute rate drove the merchant context score.",
"factors": [
{
"label": "On-time fulfillment",
"weight_pct": 38,
"direction": "positive",
"source_type": "commerce_partner"
},
{
"label": "Refund rate",
"weight_pct": 12,
"direction": "negative",
"source_type": "commerce_partner"
}
]
}
}
],
"partner_attestation": {
"has_partner_attestation": true,
"partner_attestation_count": 1,
"partner_sources": [
{
"display_name": "Registered commerce feed",
"partner_id": "partner_7k2m",
"signal_count": 12
}
]
},
"pti_provenance": {
"linked": true,
"pti_id": "pti_9A72X8K2",
"partner_id": "partner_7k2m",
"explain_api": "/api/v1/pti/explain/pti_9A72X8K2/"
}
},
"created_at": "2026-06-20T14:30:00+00:00",
"expires_at": "2026-09-20T14:30:00+00:00",
"is_expired": false
}
}

Two contexts requested → two blocks in context_intelligence. Commerce-attested signals (fulfillment, refunds) appear under merchant; repayment and platform activity under lending. Partner display names are tenant-specific — treat partner_id and source_type as stable integration keys.

Treat report (and nested trust_intelligence) as your machine-readable contract. Ignore unknown keys. PDFs are for humans — parse JSON, not PDF.

4. Intelligence (optional dedicated read)

When you only need explainability (charts, Insights Studio), call:

GET /api/v1/trust-reports/reports/<report_id>/intelligence/

Returns the same trust_intelligence.v1 document as the inline block on poll — useful when you want a smaller response or refresh explainability without re-fetching legacy report fields.

5. Download PDF (optional)

GET /api/v1/trust-reports/reports/<report_id>/download/
Accept: application/pdf

Returns application/pdf when a PDF was generated; otherwise 404. Optional query: generator=institution (default) or generator=individual.

Partner events vs report lookups

FlowAuthAPI prefix
Partner sends commerce eventsWebhook HMACPOST /api/v1/partner/webhooks/{partner_id}/
Partner reads merchant scorePublic / partner id/api/v1/pti/explain/…, embed, widget
Institution looks up an individualX-API-Key/api/v1/trust-reports/…

Producer onboarding: For partners. TCU metering: Partner billing.


From zero to first report

Start on TT_SANDBOX-… for integration work; swap to TT_LIVE-… only after verification and a deliberate go-live—see sandbox and live API keys.

export TUMITRUST_API_KEY='paste-from-hub'
export TUMITRUST_BASE_URL='https://tumitrust.com/api/v1'
export SUBJECT_ID='12345' # from directory search
export REPORT_ID='uuid-from-generate-response'

curl ↔ HTTP client cheat sheet

curl flagMeaningIn code
-sSSilent body, but show errors on failureCheck raise_for_status() / response.ok / IOException
-H "Name: value"Request headerheaders dict / HttpHeaders / CURLOPT_HTTPHEADER
-G + --data-urlencodeQuery string on a GETparams= / URLSearchParams / URI query / url.Values / .query([…]) / cpr::Parameters / $"…{Uri.EscapeDataString(…)}"
-X POST + -d '{…}'JSON bodyjson= / JSON.stringify / BodyPublishers / StringContent + JsonSerializer / bytes.NewReader + encoding/json
"$VAR/path/"Interpolated URLf-strings / template literals / String.format / format! / std::format / $"{base}/…"

1. Ping

Expect 2xx JSON. 401 → wrong or revoked key, or missing header.

curl -sS -H "Accept: application/json" -H "X-API-Key: $TUMITRUST_API_KEY" \
"${TUMITRUST_BASE_URL}/ping/"

2. Directory search (q ≥ 2 characters)

Use results.individuals[].id as SUBJECT_ID. Add phone, email, national ID, and country in production queries.

curl -sS -G -H "Accept: application/json" -H "X-API-Key: $TUMITRUST_API_KEY" \
--data-urlencode "q=Ada" \
--data-urlencode "category=individuals" \
"${TUMITRUST_BASE_URL}/directory/people/search/"
curl -sS -H "Accept: application/json" -H "X-API-Key: $TUMITRUST_API_KEY" \
"${TUMITRUST_BASE_URL}/directory/people/${SUBJECT_ID}/"

4. Start a report

contexts must be a non-empty list allowed by your plan (example: lending). Pick IDs only from Trust report context IDs—this is how you declare the slices you want scored and explained; see also Why teams pick TumiTrust reports. The finished report object is where those choices show up as structured, slice-aware signal you can surface in your own UI or policy engine.

After you start a report
  • Which value goes in the URL? When you start a report, the JSON includes a field called report_id. Use that same value in the status and download URLs until the report is finished. If the response includes other IDs, you can ignore them for polling and downloads—only report_id belongs in those paths.
  • Email when a report finishes (optional): Include "notify_ready_email": true or false in the JSON body. With an API key, we default to no completion email so automated flows do not flood the contact inbox on file; set true if you want that email. When the caller is a signed-in browser or app session, we default to yes unless you turn it off with false.
  • What to build first: For production automation, combine webhooks (for example when a trust report is ready) with lightweight polling of the report URL—hit GET …/reports/<id>/ immediately after generate, then use short intervals if needed; the read path is engineered to be fast, so you are not modeling a batch mainframe. Completion email and in-app messages are useful extras, not a substitute for webhooks or polling.
curl -sS -X POST \
-H "Accept: application/json" -H "Content-Type: application/json" \
-H "X-API-Key: $TUMITRUST_API_KEY" \
-d "{\"subject_user_id\": ${SUBJECT_ID}, \"report_tier\": \"basic\", \"contexts\": [\"lending\"]}" \
"${TUMITRUST_BASE_URL}/trust-reports/reports/generate/"

5. Read report JSON until ready

Call GET …/trust-reports/reports/<report_id>/ right away after generate—many integrations see a finished report on the first try because the pipeline is built for low-latency reads. If status is still pending, sleep briefly (the examples default to 1 second via POLL_SECS only to stay polite to shared infrastructure; 250ms–500ms with a ceiling is reasonable for interactive UIs when you control load). Use 15–60 second cadences only for background reconcilers. Parse the report subtree; PDFs are for humans, not parsers.

curl -sS -H "Accept: application/json" -H "X-API-Key: $TUMITRUST_API_KEY" \
"${TUMITRUST_BASE_URL}/trust-reports/reports/${REPORT_ID}/"

Optional shortcut search: GET …/trust-reports/search/?q= — when you have rich hints, prefer /directory/people/search/ for identity resolution.

Appendix: single-file walkthrough (cURL + bash first, then Python, JavaScript, Java, PHP, C#, Go, Rust, C++ — one runnable script per tab)
#!/usr/bin/env bash
# Requires: curl, jq — Env: TUMITRUST_API_KEY (required), TUMITRUST_BASE_URL, SEARCH_Q, POLL_SECS
set -euo pipefail
BASE="${TUMITRUST_BASE_URL:-https://tumitrust.com/api/v1}"
BASE="${BASE%/}"
KEY="${TUMITRUST_API_KEY:?export TUMITRUST_API_KEY}"
Q="${SEARCH_Q:-Ada}"
POLL="${POLL_SECS:-1}"

echo "== 1) ping"
curl -sS -H "Accept: application/json" -H "X-API-Key: $KEY" "$BASE/ping/" | jq .

echo "== 2) directory search"
RAW=$(curl -sS -G -H "Accept: application/json" -H "X-API-Key: $KEY" \
--data-urlencode "q=$Q" --data-urlencode "category=individuals" \
"$BASE/directory/people/search/")
echo "$RAW" | jq .
SID=$(echo "$RAW" | jq -er '.results.individuals[0].id')
echo "subject_id: $SID"

echo "== 3) profile row"
curl -sS -H "Accept: application/json" -H "X-API-Key: $KEY" \
"$BASE/directory/people/$SID/" | jq 'keys'

echo "== 4) generate report"
GEN=$(curl -sS -X POST -H "Accept: application/json" -H "Content-Type: application/json" \
-H "X-API-Key: $KEY" \
-d "{\"subject_user_id\":$SID,\"report_tier\":\"basic\",\"contexts\":[\"lending\"]}" \
"$BASE/trust-reports/reports/generate/")
echo "$GEN" | jq .
RID=$(echo "$GEN" | jq -er '.id // .report_id')
echo "report_id: $RID"

echo "== 5) poll until ready"
while true; do
P=$(curl -sS -H "Accept: application/json" -H "X-API-Key: $KEY" \
"$BASE/trust-reports/reports/$RID/")
ST=$(echo "$P" | jq -r '(.report.status // .status // .state // "") | ascii_downcase')
echo "poll status: ${ST:-'(empty)'}"
case "$ST" in completed|complete|ready|success) echo "$P" | jq . | head -c 4000; echo; break ;; esac
sleep "$POLL"
done

Finding someone (no guessed IDs)

Your system already has…What to send
Phonephone= or q= with digits; add country=ZM (ISO-3166 alpha-2) for local formats
Emailemail= or q=
National IDnational_id, nrc, or nin as supported in your market
Name onlyq= or first_name + last_name — expect ambiguity; add phone or ID
Only your organization's customer IDMap in your systems first, then search

Pattern: search → optional GET …/people/<id>/ → generate report. If multiple rows match, let a trained operator pick using evidence you already verified outside TumiTrust.


In-platform lookups vs API

The TumiTrust console (browser) and the REST routes on this page are the same product accessed two ways: people on your team run directory, reports, and related flows while signed in; your automation calls /api/v1/… with X-API-Key (and Bearer JWT where noted—for example webhook registration). All hosted integrations use https://tumitrust.com/api/v1/ unless your enterprise contract specifies a dedicated endpoint.


Webhooks: from zero to verified delivery

A webhook is TumiTrust calling your HTTPS URL when something important happens (for example a trust report becomes ready). For teams that want realtime or streaming-shaped UX, webhooks are the fastest completion signal: delivery is scheduled as soon as the event is durable in our systems, which is often milliseconds to a few seconds after the user-visible transition—without keeping a poll loop open. You can still GET the report immediately after generate (that path is also engineered to be low-latency); webhooks simply wake your workers the moment the platform crosses the line you care about.

Two credentials—on purpose

TaskCredential
Register / list / test / delete webhooksBearer JWT (signed-in console user)
Start and download reports from automationX-API-Key

Base path: https://tumitrust.com/api/v1/trust-signals/webhooks/

Shortest happy path

Public HTTPS URL → hub Registersave signing secretTest until 2xx → enforce HMAC verification in production → review Deliveries in the hub to confirm receipt.

Step 1 — Public HTTPS POST endpoint

Answer 2xx within a few seconds; queue heavy work after the quick response. Your endpoint must be reachable from the public internet over HTTPS. During integration testing, use a tunnel service (for example ngrok or Cloudflare Tunnel) until your production URL is ready.

Step 2 — Console session JWT

Same Authorization: Bearer … your signed-in workspace session already uses. Treat it like a password.

Step 3 — Register

POST …/trust-signals/webhooks/register/

Authorization: Bearer <access token from a signed-in console session>
Content-Type: application/json
Accept: application/json
{
"url": "https://integrations.yourorg.com/v1/tumitrust/events",
"event_types": ["trust_report.generated", "trust_report.updated"],
"description": "Production report notifications"
}

Handle unknown X-Webhook-Event-Type values safely (log, persist, return success) so new product events do not break you.

curl -sS -X POST "https://tumitrust.com/api/v1/trust-signals/webhooks/register/" \
-H "Authorization: Bearer $INSTITUTION_JWT" \
-H "Content-Type: application/json" -H "Accept: application/json" \
-d '{"url":"https://integrations.yourorg.com/v1/tumitrust/events","event_types":["trust_report.generated"],"description":"from curl"}'

Step 4 — Signing secret

Registration returns a signing secret—copy immediately. On each delivery: read raw body, compute HMAC-SHA256 (hex), time-safe compare to X-Webhook-Signature, reject with 401 on mismatch.

Step 5 — Test delivery

POST …/trust-signals/webhooks/<uuid>/test/ with the same Bearer token until your endpoint returns 2xx. Set WEBHOOK_ID to the UUID returned from register (or copy from the hub).

curl -sS -X POST \
"https://tumitrust.com/api/v1/trust-signals/webhooks/${WEBHOOK_ID}/test/" \
-H "Authorization: Bearer $INSTITUTION_JWT" \
-H "Accept: application/json"

List webhooks (same Bearer)

GET https://tumitrust.com/api/v1/trust-signals/webhooks/ — confirm registration or troubleshoot missing deliveries.

curl -sS "https://tumitrust.com/api/v1/trust-signals/webhooks/" \
-H "Authorization: Bearer $INSTITUTION_JWT" \
-H "Accept: application/json"

Step 6 — Production

On trust_report.generated / trust_report.updated, fetch fresh JSON with X-API-Key: GET …/trust-reports/reports/<report_id>/. Use delivery history in the hub when investigating failures.

Webhook quick reference

ActionCallAuth
RegisterPOST …/webhooks/register/Bearer JWT
ListGET …/webhooks/Bearer JWT
UpdatePUT / PATCH …/webhooks/<uuid>/Bearer JWT
TestPOST …/webhooks/<uuid>/test/Bearer JWT
DeliveriesGET …/webhooks/<uuid>/deliveries/Bearer JWT
DeactivateDELETE …/webhooks/<uuid>/delete/Bearer JWT

Reports always use X-API-Key on /api/v1/trust-reports/reports/….


Deeper subject signals (optional, JWT)

GET …/trust-signals/subjects/<numeric_user_id>/ returns richer portable trust JSON when enabled for your account. Requires Bearer JWT and the right subscription or pilot. Usage may be metered.

Batch: POST …/trust-signals/batch/ and GET …/trust-signals/batch/<batch_id>/status/ with the same JWT style.


Report JSON vs PDF

Treat GET …/trust-reports/reports/<report_id>/ as the machine-readable contract. New fields may appear—ignore unknown keys so you stay compatible across releases.


Partner API key authentication

Data partners and commerce integrators use partner-scoped API keys — separate from institution X-API-Key keys used for trust-report lookups.

HeaderValue
X-Partner-API-Key or X-API-KeyFull secret shown once at creation (TT_PARTNER-…)
Acceptapplication/json

Rules:

  • partner_id is derived from the key — do not pass a different partner_id query param or X-Partner-Id header; mismatches return 403.
  • Keys are hashed at rest; copy the secret when issued — it cannot be retrieved later.
  • Each key carries scopes. Request only what you need:
ScopeEndpoints
partner:billingGET /api/v1/partner/billing/usage/, invoices, forecast
trust:explainGET /api/v1/pti/explain/<pti_id>/
partner:connectorGET/PATCH /api/v1/partner/<partner_id>/connector/, event catalog
partner:signalsPOST /api/v1/partner/<partner_id>/signals/manual/
partner:widgetsWidget and embed payloads
trust:readRead-only trust score surfaces

Create and rotate keys in Partner Operations → API Keys. Unauthenticated calls to protected partner routes return 401/403.

curl -sS -H "X-Partner-API-Key: $TT_PARTNER_KEY" \
-H "Accept: application/json" \
"https://tumitrust.com/api/v1/partner/billing/usage/"

Idempotency

Mutating partner and signal endpoints accept an Idempotency-Key header (or idempotency_key in the JSON body where documented). Replays with the same key return the original outcome instead of creating duplicate rows.

SurfaceHeader / fieldNotes
Manual signal intakeIdempotency-KeyDedupes TrustSignal writes per partner + entity + event
Partner webhooksevent.idempotency_key in payloadDedupes TrustEvent before Celery ingest
Report generate (institution)Idempotency-KeySame report ack on retry (when enabled for your plan)

Guidance:

  • Use a stable client-generated UUID or deterministic string (partner:order:12345:shipped).
  • Retry 5xx and transient network errors with the same idempotency key.
  • Do not reuse keys across different logical operations.

PTI explain API (authenticated)

GET /api/v1/pti/explain/<pti_id>/ returns explain_score.v1 — structured factor breakdown, partner context metadata, and optional multi-context scores.

Authentication (one of):

  • Partner API key with trust:explain scope (recommended for server-to-server)
  • Institution console session (Bearer JWT) when the caller owns the partner linkage
  • Partner hub session tied to the same partner_id

Query parameters:

ParamDefaultDescription
contextPartner primary contextTrust context slug (merchant, lending, …)
partner_idFrom authIgnored when authenticated via partner key

Example:

curl -sS -H "X-Partner-API-Key: $TT_PARTNER_KEY" \
"https://tumitrust.com/api/v1/pti/explain/pti_9A72X8K2/?context=merchant"

Responses include schema_version, explainability factors, and partner_context_config. Explain calls are audited (pti.explain.read) and cached (~60s TTL per partner + subject + context). Unauthenticated requests return 401/403.


Screening rich output

Institution screening responses include normalized entity findings suitable for compliance review — not just headline match/clear status.

Each finding may include:

FieldDescription
aliasesAlso-known-as names (deduplicated)
jurisdictionsISO-style jurisdiction codes
jurisdiction_breakdownPer-jurisdiction programs, list entries, status
programsSanctions / watchlist program identifiers
provenanceDataset, provider, and mode (live, rehearsal, demo)

Provenance display distinguishes live federation from demo/rehearsal presets so analysts never treat synthetic data as regulatory fact. Optional checks (adverse media, business registry, identity) are labeled Skipped or Not checked when not in scope.

Integrators consuming screening JSON should preserve unknown keys and render jurisdiction_breakdown when present — legacy provider keys (aka, country) are normalized server-side.


Workflow API (preview)

Multi-step onboarding and compliance flows (KYC, hiring screening, lending decision support) are modeled in the workflow_engine app (ADR 0034). Platform presets compile to existing EventTriggerRule + Celery tasks.

Planned HTTP surface (OpenAPI tag Workflow):

MethodPathPurpose
POST/api/v1/workflows/<slug>/start/Start instance for pti_id or institution subject
GET/api/v1/workflows/instances/<instance_id>/Status, current step, SLA timers
GET/api/v1/workflows/instances/List active instances (institution-scoped)

Today: definitions and instances persist in workflow_engine_* tables; institution console Workflows hub visualizes runs. Public start/status routes ship in a follow-on release — design integrations against the DSL schema version in OpenAPI when the tag is published.


After you go live

  1. Prefer webhooks for push; on delivery, fetch the latest report JSON if you need the freshest snapshot.
  2. GET …/trust-reports/reports/stats/ with X-API-Key for simple volume summaries.
  3. Retry transient failures with backoff; design idempotency where the product allows repeated starts.
  4. Billing — credits, tiers, and limits follow your contract; watch Billing in the hub and error payloads.

Help and next steps


As integrators, design parsers to tolerate new JSON fields—that is how integrations stay healthy as the product evolves.