API / API reference
Projects
Create, read and update projects.
data. Paginated collections add
links and meta.
Errors are never wrapped; see Errors.
/api/v1/projects
Scope
projects:read
List the projects this token can reach
Every project this designer owns, newest first, paginated. Each entry carries component_count, so you can show a picker without a second call per project.
This is normally your first call after /me: a designer picks which of their games your tool should work on, and the id you get back is the {project} in almost every other path on this page.
curl "https://dustinsdesignerden.com/api/v1/projects" \
-H "Authorization: Bearer $DDD_TOKEN"
const res = await fetch('https://dustinsdesignerden.com/api/v1/projects', {
method: 'GET',
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();
$response = Http::withToken($token)->get('https://dustinsdesignerden.com/api/v1/projects');
$data = $response->json();
res = requests.get(
'https://dustinsdesignerden.com/api/v1/projects',
headers={'Authorization': f'Bearer {token}'},
)
data = res.json()
{
"data": [
{
"id": 1,
"name": "Harvest Moon",
"short_description": "A farming game.",
"description": null,
"is_locked": false,
"player_count_min": 2,
"player_count_max": 4,
"playtime_minutes": 45,
"min_age": null,
"genre": "Worker Placement",
"theme": null,
"primary_mechanic": null,
"edition": null,
"logo_url": null,
"backdrop_url": null,
"website_url": null,
"bgg_id": null,
"component_count": 0,
"created_at": "2026-08-01T16:20:38+00:00",
"updated_at": "2026-08-01T16:20:38+00:00"
}
],
"links": {
"first": "http://dustinsdesignerden.test/api/v1/projects?page=1",
"last": "http://dustinsdesignerden.test/api/v1/projects?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"links": [
{
"url": null,
"label": "« Previous",
"page": null,
"active": false
},
{
"url": "http://dustinsdesignerden.test/api/v1/projects?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": null,
"label": "Next »",
"page": null,
"active": false
}
],
"path": "http://dustinsdesignerden.test/api/v1/projects",
"per_page": 50,
"to": 1,
"total": 1
}
}
Parameters
-
per_pageinteger · query - Defaults to 50
-
pageinteger · query
Response: Pagination
-
dataarray - The page of results.
-
linksobject - first, last, prev and next URLs.
-
metaobject - current_page, last_page, per_page, from, to, total and path.
Response: Project
-
idinteger - Designer Den's id for the project. Use it in every project-scoped path.
-
namestring - Project name.
-
short_descriptionstring|null - One-line summary.
-
descriptionstring|null - Long description.
-
is_lockedboolean - A locked project is read-only. Every write returns 409 while this is true.
-
player_count_mininteger|null - Minimum players.
-
player_count_maxinteger|null - Maximum players.
-
playtime_minutesinteger|null - Typical play length.
-
min_ageinteger|null - Recommended minimum age.
-
genrestring|null - Free text.
-
themestring|null - Free text.
-
primary_mechanicstring|null - Free text.
-
editionstring|null - Free text.
-
logo_urlstring|null - Project logo. Set in the app, not through this API.
-
backdrop_urlstring|null - Project backdrop. Set in the app.
-
website_urlstring|null - Project website.
-
bgg_idstring|null - BoardGameGeek id.
-
component_countinteger - How many components the project has.
-
created_atstring - ISO 8601 timestamp.
-
updated_atstring - ISO 8601 timestamp.
/api/v1/projects
Scope
projects:write
Create a project
Subject to the designer's plan limit. A free account is capped, and exceeding it returns 403 plan_limit_reached. Surface that message rather than swallowing it.
curl -X POST "https://dustinsdesignerden.com/api/v1/projects" \
-H "Authorization: Bearer $DDD_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Second Game",
"short_description": "A quick filler."
}'
const res = await fetch('https://dustinsdesignerden.com/api/v1/projects', {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"name": "Second Game",
"short_description": "A quick filler."
}),
});
const data = await res.json();
$response = Http::withToken($token)->post('https://dustinsdesignerden.com/api/v1/projects', [
'name' => 'Second Game',
'short_description' => 'A quick filler.',
]);
$data = $response->json();
res = requests.post(
'https://dustinsdesignerden.com/api/v1/projects',
headers={'Authorization': f'Bearer {token}'},
json={
'name': 'Second Game',
'short_description': 'A quick filler.'
},
)
data = res.json()
{
"data": {
"id": 2,
"name": "Second Game",
"short_description": "A quick filler.",
"description": null,
"is_locked": false,
"player_count_min": null,
"player_count_max": null,
"playtime_minutes": null,
"min_age": null,
"genre": null,
"theme": null,
"primary_mechanic": null,
"edition": null,
"logo_url": null,
"backdrop_url": null,
"website_url": null,
"bgg_id": null,
"component_count": 0,
"created_at": "2026-08-01T16:20:39+00:00",
"updated_at": "2026-08-01T16:20:39+00:00"
}
}
Body parameters
-
namestring required - Project name. Max 255 characters.
-
short_descriptionstring - One-line summary. Max 500.
-
descriptionstring - Long description. Max 20000.
-
player_count_mininteger - 1 to 99.
-
player_count_maxinteger - 1 to 99.
-
playtime_minutesinteger - Typical play length.
-
min_ageinteger - 0 to 99.
-
genrestring - Free text.
-
themestring - Free text.
-
primary_mechanicstring - Free text.
-
editionstring - Free text.
-
website_urlurl - Project website.
-
bgg_idstring - BoardGameGeek id.
Response: Project
-
idinteger - Designer Den's id for the project. Use it in every project-scoped path.
-
namestring - Project name.
-
short_descriptionstring|null - One-line summary.
-
descriptionstring|null - Long description.
-
is_lockedboolean - A locked project is read-only. Every write returns 409 while this is true.
-
player_count_mininteger|null - Minimum players.
-
player_count_maxinteger|null - Maximum players.
-
playtime_minutesinteger|null - Typical play length.
-
min_ageinteger|null - Recommended minimum age.
-
genrestring|null - Free text.
-
themestring|null - Free text.
-
primary_mechanicstring|null - Free text.
-
editionstring|null - Free text.
-
logo_urlstring|null - Project logo. Set in the app, not through this API.
-
backdrop_urlstring|null - Project backdrop. Set in the app.
-
website_urlstring|null - Project website.
-
bgg_idstring|null - BoardGameGeek id.
-
component_countinteger - How many components the project has.
-
created_atstring - ISO 8601 timestamp.
-
updated_atstring - ISO 8601 timestamp.
/api/v1/projects/{project}
Scope
projects:read
Read one project
One project, with its component count. Use it to refresh something you already know about, for instance to check is_locked before you start writing.
A locked project rejects every write with 409 project_locked, so looking first lets you tell the designer why, instead of failing halfway through a sync.
curl "https://dustinsdesignerden.com/api/v1/projects/1" \
-H "Authorization: Bearer $DDD_TOKEN"
const res = await fetch('https://dustinsdesignerden.com/api/v1/projects/1', {
method: 'GET',
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();
$response = Http::withToken($token)->get('https://dustinsdesignerden.com/api/v1/projects/1');
$data = $response->json();
res = requests.get(
'https://dustinsdesignerden.com/api/v1/projects/1',
headers={'Authorization': f'Bearer {token}'},
)
data = res.json()
{
"data": {
"id": 1,
"name": "Harvest Moon",
"short_description": "A farming game.",
"description": null,
"is_locked": false,
"player_count_min": 2,
"player_count_max": 4,
"playtime_minutes": 45,
"min_age": null,
"genre": "Worker Placement",
"theme": null,
"primary_mechanic": null,
"edition": null,
"logo_url": null,
"backdrop_url": null,
"website_url": null,
"bgg_id": null,
"component_count": 0,
"created_at": "2026-08-01T16:20:38+00:00",
"updated_at": "2026-08-01T16:20:38+00:00"
}
}
Parameters
-
projectinteger · path required - Project id.
Response: Project
-
idinteger - Designer Den's id for the project. Use it in every project-scoped path.
-
namestring - Project name.
-
short_descriptionstring|null - One-line summary.
-
descriptionstring|null - Long description.
-
is_lockedboolean - A locked project is read-only. Every write returns 409 while this is true.
-
player_count_mininteger|null - Minimum players.
-
player_count_maxinteger|null - Maximum players.
-
playtime_minutesinteger|null - Typical play length.
-
min_ageinteger|null - Recommended minimum age.
-
genrestring|null - Free text.
-
themestring|null - Free text.
-
primary_mechanicstring|null - Free text.
-
editionstring|null - Free text.
-
logo_urlstring|null - Project logo. Set in the app, not through this API.
-
backdrop_urlstring|null - Project backdrop. Set in the app.
-
website_urlstring|null - Project website.
-
bgg_idstring|null - BoardGameGeek id.
-
component_countinteger - How many components the project has.
-
created_atstring - ISO 8601 timestamp.
-
updated_atstring - ISO 8601 timestamp.
/api/v1/projects/{project}
Scope
projects:write
Update a project's details
Updates the project's own details, such as its name or description. Send only the fields you want changed; anything you leave out is untouched.
This never affects components. Deliberately narrow: it cannot alter ownership, lock state or billing. If at least one field really changes, a project.updated webhook fires naming the fields that were written.
curl -X PATCH "https://dustinsdesignerden.com/api/v1/projects/1" \
-H "Authorization: Bearer $DDD_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"genre": "Engine Builder",
"playtime_minutes": 60
}'
const res = await fetch('https://dustinsdesignerden.com/api/v1/projects/1', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"genre": "Engine Builder",
"playtime_minutes": 60
}),
});
const data = await res.json();
$response = Http::withToken($token)->patch('https://dustinsdesignerden.com/api/v1/projects/1', [
'genre' => 'Engine Builder',
'playtime_minutes' => 60,
]);
$data = $response->json();
res = requests.patch(
'https://dustinsdesignerden.com/api/v1/projects/1',
headers={'Authorization': f'Bearer {token}'},
json={
'genre': 'Engine Builder',
'playtime_minutes': 60
},
)
data = res.json()
{
"data": {
"id": 1,
"name": "Harvest Moon",
"short_description": "A farming game.",
"description": null,
"is_locked": false,
"player_count_min": 2,
"player_count_max": 4,
"playtime_minutes": 60,
"min_age": null,
"genre": "Engine Builder",
"theme": null,
"primary_mechanic": null,
"edition": null,
"logo_url": null,
"backdrop_url": null,
"website_url": null,
"bgg_id": null,
"component_count": 0,
"created_at": "2026-08-01T16:20:38+00:00",
"updated_at": "2026-08-01T16:20:39+00:00"
}
}
Parameters
-
projectinteger · path required - Project id.
Body parameters
-
namestring - Project name. Max 255 characters.
-
short_descriptionstring - One-line summary. Max 500.
-
descriptionstring - Long description. Max 20000.
-
player_count_mininteger - 1 to 99.
-
player_count_maxinteger - 1 to 99.
-
playtime_minutesinteger - Typical play length.
-
min_ageinteger - 0 to 99.
-
genrestring - Free text.
-
themestring - Free text.
-
primary_mechanicstring - Free text.
-
editionstring - Free text.
-
website_urlurl - Project website.
-
bgg_idstring - BoardGameGeek id.
Response: Project
-
idinteger - Designer Den's id for the project. Use it in every project-scoped path.
-
namestring - Project name.
-
short_descriptionstring|null - One-line summary.
-
descriptionstring|null - Long description.
-
is_lockedboolean - A locked project is read-only. Every write returns 409 while this is true.
-
player_count_mininteger|null - Minimum players.
-
player_count_maxinteger|null - Maximum players.
-
playtime_minutesinteger|null - Typical play length.
-
min_ageinteger|null - Recommended minimum age.
-
genrestring|null - Free text.
-
themestring|null - Free text.
-
primary_mechanicstring|null - Free text.
-
editionstring|null - Free text.
-
logo_urlstring|null - Project logo. Set in the app, not through this API.
-
backdrop_urlstring|null - Project backdrop. Set in the app.
-
website_urlstring|null - Project website.
-
bgg_idstring|null - BoardGameGeek id.
-
component_countinteger - How many components the project has.
-
created_atstring - ISO 8601 timestamp.
-
updated_atstring - ISO 8601 timestamp.