| name | tumban | ||||||
|---|---|---|---|---|---|---|---|
| description | Tumban is a creator-profile compliance scanning API. Use this skill when an agent needs to submit profile URLs for ToS / prohibited-content analysis, retrieve the resulting triage report, integrate webhooks for asynchronous results, or manage organization API keys and webhook secrets. Covers every v2 endpoint, the asynchronous scan lifecycle, rate-limit semantics, webhook signature verification, and the role-aware permission model. | ||||||
| license | Proprietary. Contact hello@tumban.com for terms. | ||||||
| compatibility | Tumban v2 public API. Requires HTTPS, JSON over `application/json`, and a bearer credential (`sk_…` API key or dashboard session token). | ||||||
| metadata |
|
Tumban analyses creator profile URLs and returns a triage report
(recommendation, risk_score, confidence, reason_codes,
evidence_index, coverage). Scans are asynchronous: submission
returns a scan_id immediately, and final results are delivered via
webhook to a callback_url or polled with GET /api/v2/scans/{scan_id}.
Base URL: https://api-v2.tumban.com
All v2 endpoints mounted under: /api/v2
Every request requires Authorization: Bearer <token>. Two token kinds:
- API key —
sk_<64-hex>(67 characters total). Long-lived server-side credential. Returned exactly once at creation; Tumban stores only its SHA-256 hash. - Dashboard session token — short-lived browser credential issued by the auth provider. Used by the Tumban dashboard.
org_id (returned in Get org settings and on signed webhooks) is an
opaque, case-sensitive string with the prefix org_. Do not regex
against hex or any fixed alphabet — the body is alphanumeric and may
include mixed case.
| Method | Path | Purpose |
|---|---|---|
POST |
/api/v2/scan/deep |
Submit a single profile URL — full (deep) scan |
POST |
/api/v2/scan/quick |
Submit a single profile URL — quick scan |
POST |
/api/v2/batch/deep |
Submit up to N profile URLs — full (deep) scans |
POST |
/api/v2/batch/quick |
Submit up to N profile URLs — quick scans |
GET |
/api/v2/scans/{scan_id} |
Fetch a scan record (status + triage report) |
GET |
/api/v2/batches/{batch_id} |
Fetch aggregate batch progress |
There is no bare POST /api/v2/scan or POST /api/v2/batch — the
mode is part of the path (/deep or /quick). Both submission shapes
are otherwise identical and return the same response (plus a scan_mode
field). See Scan modes below.
| Method | Path | Purpose |
|---|---|---|
GET |
/api/v2/org/settings |
Read org settings |
PATCH |
/api/v2/org/settings |
Update default_callback_url (admin-only, dashboard session) |
POST |
/api/v2/org/webhook-secret/rotate |
Rotate webhook signing secret (admin-only, dashboard session) |
GET |
/api/v2/org/scans |
List recent scans for the org |
| Method | Path | Purpose |
|---|---|---|
POST |
/api/v2/org/api-keys |
Create a new API key (raw value shown once) |
GET |
/api/v2/org/api-keys |
List API keys (hashes + metadata) |
DELETE |
/api/v2/org/api-keys/{key_id} |
Revoke an API key |
Usage / analytics endpoints are not part of the public API. The dashboard renders usage data internally; there is no supported way to read it programmatically.
These are the high-precision facts the autogenerator must not collapse.
DELETE /api/v2/org/api-keys/{key_id}
There is no POST /api/v2/org/api-keys/{key_id}/revoke variant.
Hitting that path returns 404; hitting the correct path with POST
returns 405. The canonical form is DELETE on the bare key resource.
Successful response is 204 No Content with no body.
POST /api/v2/org/webhook-secret/rotate
POST /api/v2/webhook-secret/rotate (without /org/) does not exist
and returns 404. There are no path variants — only the /org/-prefixed
form is wired up.
API keys can call almost every endpoint. The five exceptions below
require a dashboard session — API-key auth gets 403 immediately:
| Endpoint | Required auth |
|---|---|
PATCH /api/v2/org/settings |
Dashboard session, role admin |
POST /api/v2/org/webhook-secret/rotate |
Dashboard session, role admin |
POST /api/v2/org/api-keys |
Dashboard session (any role) |
GET /api/v2/org/api-keys |
Dashboard session (any role) |
DELETE /api/v2/org/api-keys/{key_id} |
Dashboard session (role-aware — see below) |
Credential-management endpoints (the three /api-keys rows) reject
sk_… auth so a leaked key cannot mint, list, or revoke other keys.
The exact 403 detail strings differ between create/list and revoke —
see https://docs.tumban.com/api/errors for the canonical list.
Revoke is not admin-only. Both admins and members may call it from a dashboard session, but scope differs:
- Admins — may revoke any key in the organization.
- Members — may revoke only keys they created themselves. A
member targeting another user's key gets
404(same status as a missing key, to preventkey_idenumeration). - API keys (
sk_…) — always rejected with403.
The webhook signing secret is stored in plaintext on the server (it has to be — Tumban computes the HMAC on every outbound webhook). This contrasts with API keys, which are kept only as SHA-256 hashes.
Because the secret is shown to you exactly once at rotation time and
never reappears in GET /api/v2/org/settings, there is no recovery
path — capture it from the rotation response immediately. If you
lose it, rotate again and update every verifier in lockstep before
sending any more outbound traffic that would expect the old signature.
Every scan runs in one of two modes, selected by the endpoint path:
| Mode | Submit via | Pipeline | Typical latency |
|---|---|---|---|
| deep | /api/v2/scan/deep, /api/v2/batch/deep |
Full: profile data + external context search + social-media scraping + link traversal | < 2 min/scan |
| quick | /api/v2/scan/quick, /api/v2/batch/quick |
Profile data only, scored by a fast model — external search, social scraping, and link traversal are skipped | ~30 s/scan |
- Request body is identical across modes (
profile_url/profile_urls,callback_url,metadata). - The submission response and the scan record both include a
scan_modefield ("deep"|"quick"). The webhook payload does not carryscan_mode. - The triage report has the same fields in both modes. Because a
quick scan skips steps, the
coverageobject reflects what didn't run (e.g.social_links_checked: 0). - On
GET /api/v2/scans/{scan_id},scan_modemay be absent on legacy scans submitted before modes existed — treat absent asdeep. - Choose quick for cheap high-volume profile-level triage; deep when a decision needs the full external footprint.
POST /api/v2/scan/deep (or /api/v2/scan/quick)
→ { scan_id, status: "processing", submitted_at, estimated_completion, scan_mode }
(async)
→ scan runs server-side (timeout: 450 s)
→ on terminal status, Tumban POSTs to callback_url (when configured)
→ result also queryable via GET /api/v2/scans/{scan_id}
status values:
processing— in flight.completed— terminal; triage report available. May reflect a pipeline where some steps were skipped (slow page, login wall, transient model error). Thecoverageobject records what actually ran (e.g.social_links_checked,blocked_by_login). Readcoverageto detect partial pipelines; do not gate partial-handling logic on thestatusfield.failed— terminal;errorfield set.
GET /api/v2/scans/{scan_id} may also return a top-level is_banned
field (bool). It is absent until the ban-checker has evaluated the
profile (omitted, not null); then true if the upstream platform has
banned the creator (404 / 410 / redirect on the profile URL) or false
if the profile is still live. Test with "is_banned" in response.
Useful for skipping enforcement on already-banned creators.
confidence ∈ {high, medium, low}:
high— strong, corroborated evidence. Act on it directly.medium— solid signal, less corroboration. Useful, but a manual reviewer may want to spot-check on edge cases.low— a thin or borderline lead. Treat as review-worthy, not as a verdict.
A no_flags recommendation does not guarantee high confidence.
A profile clear except for a single thin signal — or one that hit an
infrastructure fallback (ANALYSIS_ERROR, neutral score) — can come
back no_flags with medium or low confidence. Only a profile with
no signal at all is guaranteed high. Read confidence directly;
never infer it from the recommendation tier.
How Tumban arrives at each level is not part of the public contract
and may change without notice. Build against confidence,
risk_score, recommendation, reason_codes, and evidence_index;
ignore any other field on the response.
| Range | Recommendation |
|---|---|
| 0–10 | no_flags |
| 11–40 | review_low |
| 41–60 | review_medium |
| 61–100 | review_high |
risk_score reflects confidence that a policy violation is present
(not uncertainty); missing or unreachable data does not push the score
up — partial coverage surfaces in the coverage object, never as
inflated risk.
The four enum values above are frozen API contract. The score thresholds may be tuned over time as detection improves; the enum strings will not change. How the score is produced internally is not part of the contract.
When an org has a daily_scan_limit configured, exceeding it returns
429 with a structured detail body (not a string):
{
"detail": {
"error": "daily_scan_limit_exceeded",
"limit": 1000,
"used": 1000
}
}- Counter resets at
00:00 UTC. - Batch submissions have a partial-acceptance path: when remaining
capacity is less than the requested batch size, the batch is accepted
with the leading N profiles and the response sets
daily_limit_truncated: trueplusprofiles_skipped: <int>. Only a zero-capacity submission returns429. - Tumban does not return
X-RateLimit-*headers and does not honourIdempotency-Key. Use the returnedscan_id(or per-profile scan ids in a batch) as the natural idempotency key in your handler.
Tumban POSTs JSON to your callback_url when a scan reaches a terminal
status. Up to 3 attempts; backoff 1s then 2s. Retry-After is
honoured when numeric (decimal seconds allowed, e.g. 2.5); HTTP-date
form is not parsed.
| Header | Meaning |
|---|---|
X-Tumban-Signature |
sha256=<hex> over the raw body bytes (V1). |
X-Tumban-Signature-V2 |
sha256=<hex> over "{timestamp}.{org_id}." + body (raw bytes concatenation). Recommended. |
X-Tumban-Timestamp |
Unix seconds when the payload was signed. |
X-Tumban-Org-Id |
The org_id this webhook is for. Treat as opaque. Under rare error paths Tumban may send an empty value ("") — V2 verifiers reject these because they will not match EXPECTED_ORG_ID. |
A correct V2 verifier performs three checks:
- Tenant binding —
X-Tumban-Org-Idmatches the receiver's expectedorg_id. - Replay protection —
X-Tumban-Timestampis within ~5 minutes of now. - Constant-time signature compare — never use
==on the hex digest.
POST /api/v2/scan/deep(or/api/v2/scan/quick) withprofile_url,callback_url, optionalmetadata(any JSON, echoed back).- Receive webhook at
callback_url. Verify the V2 signature first; then processrecommendation,risk_score,evidence_index, andcoverage. - Acknowledge with any
2xxstatus. Non-2xx triggers retry.
POST /api/v2/batch/deep(or/api/v2/batch/quick) withprofile_urls— every profile in the batch runs in that mode. Per-profile webhooks fire independently as each scan completes.- Poll
GET /api/v2/batches/{batch_id}for aggregate counts (completed,failed,in_progress). - If
daily_limit_truncated: trueis set on the submission response, the trailingprofiles_skippedURLs were not queued — resubmit them after the next00:00 UTC.
POST /api/v2/org/webhook-secret/rotatefrom a dashboard admin session. Capturewebhook_secretfrom the response (shown once).- Store the secret in your secret manager alongside the
org_idyour receiver expects. - Implement V2 verification — see
https://docs.tumban.com/webhooks/signaturesfor reference verifiers in Python / Node / Ruby. - Roll out the verifier before the secret is actively used by senders. Tumban switches signing to the new secret immediately on rotation; old secrets become inactive at the same instant.
POST /api/v2/org/api-keysto create a new key. Capture the rawsk_…value (shown once).- Deploy the new key. Both keys are valid simultaneously.
- Once traffic has cut over (watch
last_used_aton the old key viaGET /api/v2/org/api-keys), callDELETE /api/v2/org/api-keys/{old_key_id}from a dashboard session to revoke. There is no auto-expiry.
- Scan mode is in the path, not the body. Submit to
/api/v2/scan/deep|/scan/quick|/batch/deep|/batch/quick. There is no bare/api/v2/scanor/api/v2/batch. Body and response shapes are identical; the response addsscan_mode. - Dedup is scoped per (org, canonical URL, scan mode). A deep scan and a quick scan of the same URL coexist as separate records, each kept current independently. Re-submitting the same URL in the same mode archives the prior scan and starts a new one.
- Partial pipelines surface in
coverage, not instatus. A scan with step failures still reportsstatus: "completed"— read thecoverageobject to see what ran. Quick scans also surface their skipped steps (link traversal, social scraping, external search) incoverage. - Webhook secret is plaintext server-side. Treat any leak as a full compromise of webhook authenticity; rotate immediately.
- Revoke is
DELETE, notPOST+/revoke. And the path has no/revokesuffix — see Endpoint details above. - Webhook rotate path includes
/org/./api/v2/webhook-secret/rotate(no/org/) does not exist. - No
X-RateLimit-*headers, noIdempotency-Key. Plan around the structured 429 body andscan_idas natural idempotency key. org_idis opaque. Do not regex against hex or any specific alphabet. Compare for exact equality.no_flagsdoes not implyhighconfidence. Ano_flagsscan can carrymediumorlowconfidence (a thin single signal, or anANALYSIS_ERRORfallback). Readconfidencedirectly.
- Full reference docs:
https://docs.tumban.com - Status values:
https://docs.tumban.com/reference/status - Reason codes:
https://docs.tumban.com/reference/reason-codes - Webhook signature verifiers:
https://docs.tumban.com/webhooks/signatures - Errors and rate-limit detail:
https://docs.tumban.com/api/errors