How to Write Bug Bounty Reports That Get Paid
What triagers actually look at, why clear repro steps and demonstrated impact get you paid faster and higher, plus a report template that works.
We triage bug bounty reports for a living: thousands upon thousands of them, across self-hosted programs and every major platform. So take this as intelligence from the other side of the queue: most researchers lose money not because their bugs are bad, but because their reports make the bugs look worse than they are, or make the triager work harder than they need to.
The economics are simple. A report that reproduces first try, with obvious impact, gets validated in under an hour, and everything downstream (bounty decision, severity discussion, payment) starts that day. A report that's technically valid but needs three clarification round-trips takes two weeks of wall-clock time and arrives at the bounty decision with everyone mildly annoyed. The same bug earns different money on a different timeline, depending on the report.
What the triager actually looks at
When your report lands, a triager is doing roughly this workflow, and in the first pass they're answering three questions:
- What can an attacker do? Concretely. "Read any user's private messages" is an answer. "Broken access control on the messages endpoint" is a mechanism waiting for an answer.
- What does the attack require? Account tier, victim interaction, preconditions. This is where severity actually gets decided, so if you don't state prerequisites clearly, the triager will assume the worst-case interpretation. Worst for you, that is.
- Can I reproduce this before my coffee goes cold? Every ambiguity in your steps is a fork in the road where the triager can end up in a different state than you were in, fail to reproduce, and bounce the report back.
Notice what's not on the list: your CVSS calculation, your OWASP references, your two paragraphs about the history of the vulnerability class. Triage teams skip all of it. It's not held against you, but every screen of filler between them and the repro steps delays validation, and validation is when the money starts moving.
The template
This structure maps one-to-one onto what triage needs, in the order they need it:
# [Vuln class] in [component] allows [concrete attacker outcome]
## Summary
Two or three sentences. What's broken, where, and what an
attacker gains. No background essays.
## Impact
What an attacker actually achieves, demonstrated below.
State the blast radius honestly: which users, which data,
what preconditions.
## Prerequisites
- Account type/tier needed (attacker and victim)
- Any required state (e.g. victim has 2FA disabled)
- User interaction required: yes/no (if yes, exactly what)
## Steps to reproduce
1. Log in as any user (tested with a free-tier account).
2. Capture the request to `GET /api/v2/documents/{id}`.
3. Replace {id} with a document ID belonging to another
user (IDs are sequential; see request/response below).
4. Observe full document contents in the response.
## Proof of concept
Raw request and response, trimmed to what matters:
```http
GET /api/v2/documents/48213 HTTP/1.1
Host: app.example.com
Authorization: Bearer <attacker-token>
```
```http
HTTP/1.1 200 OK
Content-Type: application/json
{"id":48213,"owner_id":9107,"title":"Q3 board deck", ...}
```
Attacker account: [email protected] (user 8801)
Victim account: [email protected] (user 9107)
Tested: 2026-05-28, ~14:00 UTC
## Suggested severity
High. Any authenticated user can read any document across
all tenants; IDs are enumerable. No victim interaction.
(Reasoning, not just a number. See below.)
## Remediation suggestion (optional, one line)
Scope document lookup to the authenticated user's tenant.
Details that do disproportionate work:
- Test account identifiers and timestamps. The triager can find your traffic in the logs, which converts "claims" into "corroborated" instantly. In an era of AI-generated fake reports, evidence that you verifiably ran the attack is worth real money: the fastest-validated reports are the ones that are obviously real.
- Raw requests, trimmed responses. Full requests so nothing is ambiguous; responses cut to the lines that prove the point, with
...for the rest. Nobody wants 400 lines of JSON, and nobody trusts a screenshot of a status code. - Prerequisites stated up front. If your bug needs an org-admin attacker, say so in line one of prerequisites. The triager will find out anyway, and finding out from you reads as honesty while finding out despite you reads as inflation.
The mistakes that cost you money
The same handful of failures account for most slow, downgraded, or dead reports.
Theoretical impact. "An attacker could potentially escalate this to account takeover." Could they? Did you? If escalation is possible, demonstrate it and get paid for the escalated impact. If you assert it without demonstrating it, you get paid for what you demonstrated, and you've taught the triager to discount your claims.
Missing prerequisites. The report works only from a paid workspace with a specific feature flag, and you didn't mention it. The triager fails to reproduce on a free account, asks, waits, retries. You just added a week and started the severity conversation on the back foot.
Video-only PoC. Video is fine as a supplement. As the only PoC, it's a liability: triagers can't copy requests out of a video, can't diff it against the API's real behaviour, and can't hand it to engineering. Nobody wants to scrub through four minutes of your cursor hovering over Burp to transcribe a request by pausing frames. Requests and responses in text, always; video for the parts where timing or UI state genuinely matters.
Severity inflation. Titling a self-XSS "Critical account takeover" doesn't anchor the negotiation high. It anchors you low, permanently, in the mental file the triage team keeps on every recurring submitter. Triage teams calibrate on submitters over time; the researcher whose "High" always means High gets the benefit of the doubt in genuinely ambiguous cases. That reputation is worth more than any single inflated bounty attempt.
Spraying variants as separate reports. The same root cause on five endpoints is one report with five affected endpoints, and a stronger one, because it proves the flaw is systemic. Five separate reports will be deduplicated into one payout anyway, minus the goodwill.
Severity: argue impact, not arithmetic
The severity section deserves its own note, because researchers keep bringing a calculator to a judgement fight.
CVSS vectors and the Bugcrowd VRT are shared vocabulary, and using them correctly signals competence. But no triager has ever been persuaded by vector-string arithmetic: "AV:N/AC:L means this is a 9.8" convinces nobody, because every contested letter in that string is a claim about impact and likelihood wearing a costume. Programs adjust CVSS output based on context and business impact, and they're right to.
What persuades is an impact argument in plain language:
"Any free-tier user can read any tenant's documents with no interaction. IDs are sequential, so full-corpus enumeration is a for-loop. The documents include contracts and financial data. That's why I've proposed High."
Every clause is checkable, and checkable claims are what triagers pay on. If you want the higher rating, demonstrate the thing that justifies it (show the enumeration works, show the sensitive record) rather than arguing the vector string harder.
Disagreeing without dying on the hill
Sometimes triage gets it wrong. It happens; triage is judgement under volume. What you do next determines whether it gets fixed.
- Add information, not adjectives. The only reopener that works is new evidence: a working escalation, a demonstrated bypass of the cited mitigation, proof the prerequisite they're leaning on isn't actually required. "I've now demonstrated the enumeration at scale, see attached" reopens conversations. "This is absolutely a critical and your team is wrong" closes them.
- One clean escalation, in channel. If the response to new evidence still seems wrong, ask once for a second review, politely, in the report thread. On managed platforms, mediation exists. Use it after the in-thread route fails, not as an opening move.
- Know when it's a policy verdict, not a technical one. "Valid but out of scope" or "accepted risk" isn't a reproduction dispute, and arguing the technical merits harder won't move it. Take the points, file the intel, move on.
- Never go public first. Threatening full disclosure mid-negotiation converts you from researcher to adversary in one message, torches the exact reputation that pays compounding dividends, and, on most platforms, violates the terms you agreed to. The researchers earning the most on any given program are almost never the loudest ones; they're the ones triage trusts.
Before you hit submit
A thirty-second self-check that catches most of the failures above:
- Could a stranger reproduce this from the steps alone, with no access to your head? Reread step by step and hunt for the assumption you didn't write down.
- Does the title claim exactly what the PoC demonstrates, no more?
- Are prerequisites and account tiers stated explicitly?
- Are the raw request and response in the report as text?
- Did you include your test account details and a timestamp?
- Does your severity section contain a sentence of reasoning, not just a number?
If the answer to all six is yes, your report is already in the top decile of any queue we've ever worked.
One more asymmetry worth knowing: triagers remember good reporters. When your name is attached to a history of accurate, honest, reproducible reports, your future submissions get read charitably, triaged early, and argued for internally. Every report is also an investment in that.
TRIAGERS™ runs on-demand triage teams for bug bounty programs, which means we're the ones reading your reports, and we'd genuinely rather pay you fast than argue. If you run a program and want triage that treats researchers this way, get in touch.