Reveal one credential's password, and record that it was read
The password behind a hash, in clear, for a credential belonging to the calling organisation.
Every other endpoint returns it masked. This one does not, which is why it is the one act in the product that writes to the access journal as sensitive: a client asking a year later who read his collaborators' passwords gets an answer with a name, a time and an IP.
Scoped like every other read: a hash belonging to another organisation returns 404, never someone else's secret.
Authorization
ApiKeyAuth API key for authentication
In: header
Query Parameters
The leak hash to reveal
"root_domain""root_domain" | "email_domain" | "username"Response Body
application/json
application/json
curl -X GET "https://api.stealed.io/leaks/details/password?hash=string"null{
"detail": [
{
"loc": [
"string"
],
"msg": "string",
"type": "string"
}
]
}Retrieve paginated leak details with optional search GET
Retrieve paginated, deduplicated leak details for the current organization. Results are **deduplicated by hash**: each unique credential (username + password + domain) appears once, with aggregated metadata from all occurrences. **Specific Parameters:** | Parameter | Default | Description | |-----------|---------|-------------| | `identifier_column` | *required* | `root_domain`, `email_domain`, or `username` | | `page` | 1 | Page number | | `page_size` | 50 | Items per page (max: 200) | | `search` | - | Full-text search across username, host, domain, email_domain, root_domain, ip_address, computer_name, machine_user | | `sort_by` | `source_count` | Sort order: `source_count` (most seen first) or `last_seen` (most recent first) | | `min_occurrences` | - | Only return credentials seen in N+ distinct sources | Plus all standard filters (see API description above). **Response Fields:** | Field | Type | Description | |-------|------|-------------| | `username` | string | Username or email. The local part (before the `@`) is not returned separately, derive it client-side if needed | | `password` | string | Masked password: 12-hex HMAC-SHA256 fingerprint, or first and last character with stars when `password_format=hidden`. Clear value when `unmask` opened this row's domain | | `password_format` | string | How to read `password`: `fingerprint`, `hidden` (first and last character, stars in between) or `clear` | | `types` | string[] | All distinct leak types for this credential, e.g. `["Combo", "Stealer"]`. A credential found in both Combo dumps and Stealer logs will have both values. | | `type` | string | **Deprecated** (flagged in the schema). Single type value, the most recent ingestion's. Use `types`. | | `last_seen` | datetime | Most recent detection date (aggregated `max(upload_stealed)` across all occurrences) | | `upload_date` | datetime | **Deprecated** (flagged in the schema). Source file publication date. Duplicates `last_seen` on the default per-hash read; use `last_seen` | | `log_date` | datetime | Infostealer log creation date, **as declared by the malware**: sparse and partly bogus (see the note below). Representative value | | `first_seen` | datetime | Earliest detection date (`min(first_seen)`, the credential's global first discovery, NOT `min(upload_stealed)`: it can predate this tenant's earliest occurrence) | | `source_count` | int | **Deprecated** (flagged in the schema). Legacy proxy metric `uniq(file_name, stealer_name, country)`. Prefer `sources`/`occurrences`/`files`. | | `sources` | int | Number of distinct sources reporting this credential (`uniqExactIf(telegram_channel)`, floored by the backfill HLL snapshot and by 1), from `leaks_matched` | | `occurrences` | int | Number of distinct (source, file) detections all-time (`uniqExact((telegram_channel, file_name))`) for this credential | | `files` | int | Number of distinct named files (`uniqExactIf(file_name, going-forward)`) this credential was found in | | `domain` | string | Associated domain | | `host` | string | Full URL or host | | `email_domain` | string | Email domain part | | `root_domain` | string | Root domain | | `country` | string | ISO 2-letter country code. Representative value (see note below) | | `stealer_name` | string | Stealer family (RedLine, Raccoon, etc.). Representative value | | `software` | string | Browser/app. Representative value | | `protocol` | string | Protocol (https, android, ...). Representative value | | `ip_address` | string | Victim IP address. Representative value | | `computer_name` | string | Machine name. Representative value | | `machine_id` | string | Machine id. Representative value | | `machine_user` | string | Machine user account. Representative value | | `hardware_id` | string | Hardware id. Representative value | | `log_date` | datetime | Date of the infostealer log. Representative value | | `countries` | string[] | EVERY country this credential was seen from | | `stealers` | string[] | EVERY stealer family | | `softwares` | string[] | EVERY browser/app | | `protocols` | string[] | EVERY protocol | | `hosts` | string[] | EVERY host, normalised to a bare hostname | | `ip_addresses` | string[] | EVERY victim IP | | `machine_users` | string[] | EVERY machine user account | | `computer_names` | string[] | EVERY machine name | | `machine_ids` | string[] | EVERY machine id | | `hardware_ids` | string[] | EVERY hardware id | | `hash` | string | Unique deduplication hash | **Note:** Results are deduplicated by hash. The raw `upload_stealed` column is not returned directly. Instead, `first_seen` and `last_seen` are computed as `min(upload_stealed)` and `max(upload_stealed)` across all occurrences of each credential. **About `log_date`.** It is written by the malware inside the stealer log, not by us: only ~0.2% of raw occurrences carry one (~44% of credentials once aggregated), and a few are dated in the future. Treat it as indicative. **Representative values vs sets.** One credential (hash) is typically seen in several infostealer logs, and the machine-level values can differ between them. Each such field is returned twice: the singular field (`software`, `country`, `ip_address`, ...) carries ONE representative value, the most recent occurrence's, and the plural field (`softwares`, `countries`, `ip_addresses`, ...) carries the deduplicated set of EVERY value seen across all occurrences. Empty values are dropped, so a Combo-only credential returns `[]` and never `[""]`. A credential seen in three logs from three countries returns `"country": "FR"` and `"countries": ["FR", "MX", "ID"]`. These sets are materialised per credential in the read model, so they cost nothing extra to return. What they do NOT give is the pairing between values (which IP went with which browser, on which date): for that, call `GET /leaks/details/occurrences?hash=...`, which returns every (source, file) detection with its own machine metadata. **Response Format:** ```json { "data": [ { "username": "user@example.com", "password": "a91f3c8b2d47", "password_masked": true, "password_format": "fingerprint", "types": ["Stealer"], "type": "Stealer", "first_seen": "2025-01-15T08:30:00", "last_seen": "2025-03-01T12:00:00", "host": "login.example.com", "domain": "login.example.com", "protocol": "https", "email_domain": "example.com", "root_domain": "example.com", "upload_date": "2025-02-28T10:00:00", "log_date": "2025-02-27T14:00:00", "machine_id": "DESKTOP-ABC1234", "computer_name": "LAPTOP-XYZ", "hardware_id": "hwid-1234-5678", "machine_user": "john.doe", "ip_address": "192.168.1.10", "country": "FR", "software": "chrome, profile: 0", "stealer_name": "RedLine", "countries": ["FR", "BE"], "stealers": ["RedLine", "Lumma"], "softwares": ["chrome, profile: 0", "Opera GX, Profile: 0"], "protocols": ["https"], "hosts": ["login.example.com"], "ip_addresses": ["192.168.1.10", "10.0.0.4"], "machine_users": ["john.doe"], "computer_names": ["LAPTOP-XYZ"], "machine_ids": ["DESKTOP-ABC1234"], "hardware_ids": ["hwid-1234-5678"], "hash": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", "source_count": 3, "sources": 2, "occurrences": 3, "files": 3 } ], "total": 12345, "page": 1, "page_size": 50, "total_pages": 247 } ``` **Examples:** ```bash # Basic paginated request GET /leaks/details?identifier_column=root_domain&page=1&page_size=50 # Sort by most recent GET /leaks/details?identifier_column=root_domain&sort_by=last_seen # Sort by number of distinct sources (leaks_matched-derived) GET /leaks/details?identifier_column=root_domain&sort_by=sources # Only credentials seen 3+ times GET /leaks/details?identifier_column=root_domain&min_occurrences=3 # Full-text search with filters GET /leaks/details?identifier_column=root_domain&search=john&type=Stealer&country=FR # Custom date range GET /leaks/details?identifier_column=root_domain&start_date=2024-01-01&end_date=2024-12-31&page_size=200 ``` **Pagination (retrieve all results):** ```bash # Page 1 GET /leaks/details?identifier_column=root_domain&page=1&page_size=200 # Page 2 GET /leaks/details?identifier_column=root_domain&page=2&page_size=200 # ... continue until page >= total_pages ```
Retrieve every occurrence (source + file) for a specific leak hash GET
Retrieve every distinct detection of a deduplicated credential — one row per `(telegram_channel, file_name)` pair — read directly from the tenant-routed `leaks_matched_*` table (no separate occurrence store, no JOIN). The row count EQUALS the `occurrences` figure shown in the `/leaks/details` list (same grain), across ALL types (Combo and Stealer). Use this after `/leaks/details` to drill into a credential and see each detection. The proprietary `telegram_channel` and `file_name` are returned (`channel`, `file_name`, `source_type`) only to a USER SESSION of an organisation holding the `source_reveal` feature (on by default for a workspace, off for a client), never to an API key. Otherwise the source is replaced by a stable opaque id (`opaque_source_id`, `"Source #" + md5(channel)[:6]`) and, when the detection carried a published file, an opaque lot id (`opaque_lot_id`, `md5(file_name)[:6]`). Backfilled / historical detections have no file, hence no lot. `computer_name` is never selected nor returned. `date` is `max(upload_stealed)` for that detection, i.e. **when Stealed ingested it** — the same clock as the list's `first_seen` / `last_seen`, so the two views can never contradict each other. It is deliberately NOT the source's own publication timestamp (`upload_date`, the Telegram message date), which runs hours to days ahead of ingestion and is unset on some rows. **Parameters:** - `hash` (required): The leak hash from the `/leaks/details` response - `tenant_id_override` (optional): workspace (MSSP) only — inspect a child tenant's detail; validated against the caller's portfolio (403/404 otherwise) Tenant isolation: the query is scoped `WHERE tenant_id = <server-derived> AND hash = <param>`, so a hash belonging to another tenant returns nothing. **Response:** ```json { "total_occurrences": 2, "occurrences": [ {"date": "2026-06-01T10:30:00", "source": "Source #a1b2c3", "lot": "d4e5f6", "type": "Stealer", "country": "FR", "stealer_name": "RedLine", "software": "Chrome", "ip_address": "1.2.3.4", "machine_user": "admin", "machine_id": "…", "protocol": "https"}, {"date": "2026-05-20T08:00:00", "source": "Source #99aa11", "lot": null, "type": "Combo", "country": null, "stealer_name": null, "software": null, "ip_address": null, "machine_user": null, "machine_id": null, "protocol": null} ] } ``` **Example:** ```bash GET /leaks/details/occurrences?hash=5d41402abc4b2a76b9719d911017c592 ```