Open standard
CTFProfile Event Results Standard
A single, platform-neutral JSON format for importing completed-event results into
CTFProfile. Conform your scoring data to this shape and it imports cleanly — every
section is optional, and anything missing is flagged to the event's organizers to
fill in later. Current version: ctfprofile-results/v1.
Top-level shape
One JSON object with these arrays. Include only what you have.
{
"format": "ctfprofile-results/v1",
"event": { "name": "Example CTF 2025" },
"challenges": [ ... ],
"teams": [ ... ], // each may contain members[]
"players": [ ... ], // solo competitors
"solves": [ ... ],
"hints": [ ... ], // optional
"opens": [ ... ] // optional
}
challenges[]
Every challenge in the event.
| Field | Requirement | Meaning |
|---|---|---|
id |
recommended | Stable id referenced by solves/hints/opens. |
name |
required | Challenge name. |
category |
recommended | e.g. web, pwn, crypto — drives skill mapping. |
points |
recommended | Point value (integer). |
description |
optional | Markdown allowed. |
author |
optional | Challenge author / attribution. |
teams[]
Team entries. Omit for solo-only events.
| Field | Requirement | Meaning |
|---|---|---|
id |
required | Stable id referenced by solves. |
name |
required | Team name (used to match a registered team). |
ctfprofile_token |
optional | Pre-event link token — most reliable match. |
score |
optional | Final score. |
rank |
optional | Final placement. |
members |
optional | Array of player objects (same shape as players[]). |
players[] (and team members[])
Individual competitors. Solo players go in players[]; team members go in their team's members[].
| Field | Requirement | Meaning |
|---|---|---|
id |
recommended | Stable id referenced by solves. |
name |
required | Username (used to match a site account). |
ctfprofile_token |
optional | Pre-event link token — most reliable match. |
score |
optional | Final score (solo players). |
rank |
optional | Final placement (solo players). |
solves[]
One row per correct solve.
| Field | Requirement | Meaning |
|---|---|---|
challenge_id |
required | Refers to a challenge id. |
team_id / user_id |
required | Who solved it (one or both). |
timestamp |
recommended | ISO-8601; powers solve-time & first-blood stats. |
id |
optional | Submission id; de-duplicates re-imports. |
hints[] · opens[]
Optional. Power hint-usage and time-to-solve-from-open stats.
| Field | Requirement | Meaning |
|---|---|---|
challenge_id |
required | Refers to a challenge id. |
user_id |
recommended | Who unlocked/opened it. |
timestamp |
recommended | ISO-8601. |
Matching & timestamps
- Players/teams are matched in order: CTFProfile token → platform username → site username (case-insensitive).
- Teams that don't match a registered site team become claimable results a captain can claim later — no data is lost.
- Timestamps are ISO-8601; a missing timezone is treated as UTC.
- Sensitive fields (passwords, emails, IPs, raw flag text) are ignored on import and never stored.
Complete example
{
"format": "ctfprofile-results/v1",
"event": {
"name": "Example CTF 2025"
},
"challenges": [
{
"id": "c1",
"name": "Cookie Monster",
"category": "web",
"points": 100,
"description": "Auth bypass via an unsigned session cookie.",
"author": "webwyrm"
},
{
"id": "c2",
"name": "Heap Lantern",
"category": "pwn",
"points": 450,
"description": "Tcache poisoning into code execution.",
"author": "cipherlynx"
}
],
"teams": [
{
"id": "t1",
"name": "Null Pointers",
"score": 550,
"rank": 1,
"members": [
{
"id": "u1",
"name": "cipherlynx"
},
{
"id": "u2",
"name": "webwyrm"
}
]
}
],
"players": [
{
"id": "u9",
"name": "soloist",
"score": 100,
"rank": 2
}
],
"solves": [
{
"id": "s1",
"challenge_id": "c1",
"team_id": "t1",
"user_id": "u2",
"timestamp": "2025-01-01T10:15:00Z"
},
{
"id": "s2",
"challenge_id": "c2",
"team_id": "t1",
"user_id": "u1",
"timestamp": "2025-01-01T12:40:00Z"
},
{
"id": "s3",
"challenge_id": "c1",
"user_id": "u9",
"timestamp": "2025-01-01T11:05:00Z"
}
],
"hints": [
{
"challenge_id": "c2",
"user_id": "u1",
"timestamp": "2025-01-01T12:10:00Z"
}
],
"opens": [
{
"challenge_id": "c1",
"user_id": "u2",
"timestamp": "2025-01-01T10:00:00Z"
}
]
}