Skip to content

Create a project

POST
/v1/projects
curl --request POST \
--url https://api.pealboard.com/v1/projects \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "title": "example", "shortDescription": "example", "public": true, "statusOptions": [ "Todo", "In progress", "Done" ], "issueIds": [ "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0" ] }'

Creates a GitHub Project (v2) on the account this workspace is connected to, then caches it with its fields and its items. It is a GitHub project in every respect: it is at github.com, the engineering team can open it there, and deleting Pealboard leaves it behind.

On an organization the Pealboard App creates it. On a personal account it cannot: the App’s Projects permission is an organization permission, and GitHub grants no personal-account equivalent — not to the App, and not to your own linked GitHub account either, because a GitHub App’s user-to-server token carries the App’s permissions rather than your own. 403 before anything reaches GitHub, naming the connected account and the page to create the project on GitHub instead; Pealboard picks it up on the next sync.

statusOptions renames the built-in Status options, which GitHub creates as Todo, In Progress and Done. The names you send are the names GitHub holds. The colours of the first options are kept and any beyond them are given one from GitHub’s palette; the descriptions GitHub writes for its own words are cleared.

public cannot be set by the App. GitHub refuses a visibility change from an installation token whatever the value, so public: true on an organization is a 403 with the remedy rather than a write that silently does nothing. Change it in the project’s own settings on GitHub.

Send Idempotency-Key to make a retry return the first answer rather than creating a second project.

Access. Requires the member role or above (member, admin, owner). A project created here is created in the customer’s own GitHub account, under their installation. API-key scope: projects:write.

Billing. Refused with 402 while the workspace’s billing has lapsed. Reads continue and nothing is deleted.

Retries. Send Idempotency-Key and a retry with the same key within 24 hours returns the first answer rather than acting twice.

Media typeapplication/json
object
title
required
string
>= 1 characters <= 256 characters
shortDescription

The line under the title on GitHub’s own project list. GitHub ignores a blank one, so an empty string is refused here rather than accepted and dropped.

string
>= 1 characters <= 1024 characters
public

Refused with 403 when Pealboard is writing as the App, which is every organization installation. GitHub does not let an App change a project’s visibility.

boolean
statusOptions

Two to ten names, all different. Renames the built-in Status options. Omit to keep GitHub’s Todo, In Progress and Done.

Array<string>
>= 2 items <= 10 items
Example
[
"Todo",
"In progress",
"Done"
]
issueIds

Issues to put on the project, from GET /v1/issues. Each becomes an item; the field values a project’s built-in workflow sets on add arrive with the projects_v2_item webhook.

Array<string>
<= 50 items

The project, as GET /v1/projects/{projectId} returns it.

Media typeapplication/json
object
id
required

A UUID this API issued.

string format: uuid
nodeId
required
string
number
required
number
title
required
string
shortDescription
required
string | null
ownerLogin
required
string
public
required
boolean
closed
required
boolean
url
required

The project on GitHub.

string format: uri
fields
required
Array<object>
object
id
required

A UUID this API issued.

string format: uuid
nodeId
required

What every field value is keyed by. Survives a rename.

string
name
required
string
dataType
required

GitHub’s ProjectV2FieldType: TEXT, SINGLE_SELECT, NUMBER, DATE, ITERATION, and the rest.

string
options
required

In the order the project shows them. Null unless the field is a single select.

Array<object> | null
object
id
required
string
name
required
string
color
required
string
description
required
string
isIssueField
required

True when the field is an organization issue field linked into this project.

boolean
itemCount
required
number
syncedAt
required
string format: date-time
Examplegenerated
{
"id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"nodeId": "example",
"number": 1,
"title": "example",
"shortDescription": "example",
"ownerLogin": "example",
"public": true,
"closed": true,
"url": "https://example.com",
"fields": [
{
"id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"nodeId": "example",
"name": "example",
"dataType": "example",
"options": [
{
"id": "example",
"name": "example",
"color": "example",
"description": "example"
}
],
"isIssueField": true
}
],
"itemCount": 1,
"syncedAt": "2026-04-15T12:00:00Z"
}

No valid credentials.

Media typeapplication/json
object
error
required
object
code
required

A stable identifier for the kind of failure. Safe to branch on.

string
message
required

What went wrong, written for a person.

string
detail

Structured particulars, when there are any.

object
key
additional properties
remedy

What to do about it, when there is something to do.

string
requestId
required

Quote this when asking about a specific failure.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"detail": {
"additionalProperty": "example"
},
"remedy": "example",
"requestId": "example"
}
}

The workspace’s billing has lapsed, so writes are refused.

Media typeapplication/json
object
error
required
object
code
required

A stable identifier for the kind of failure. Safe to branch on.

string
message
required

What went wrong, written for a person.

string
detail

Structured particulars, when there are any.

object
key
additional properties
remedy

What to do about it, when there is something to do.

string
requestId
required

Quote this when asking about a specific failure.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"detail": {
"additionalProperty": "example"
},
"remedy": "example",
"requestId": "example"
}
}

Authenticated, but not permitted.

Media typeapplication/json
object
error
required
object
code
required

A stable identifier for the kind of failure. Safe to branch on.

string
message
required

What went wrong, written for a person.

string
detail

Structured particulars, when there are any.

object
key
additional properties
remedy

What to do about it, when there is something to do.

string
requestId
required

Quote this when asking about a specific failure.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"detail": {
"additionalProperty": "example"
},
"remedy": "example",
"requestId": "example"
}
}

No such resource, or it is not visible to you.

Media typeapplication/json
object
error
required
object
code
required

A stable identifier for the kind of failure. Safe to branch on.

string
message
required

What went wrong, written for a person.

string
detail

Structured particulars, when there are any.

object
key
additional properties
remedy

What to do about it, when there is something to do.

string
requestId
required

Quote this when asking about a specific failure.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"detail": {
"additionalProperty": "example"
},
"remedy": "example",
"requestId": "example"
}
}

The request conflicts with the current state.

Media typeapplication/json
object
error
required
object
code
required

A stable identifier for the kind of failure. Safe to branch on.

string
message
required

What went wrong, written for a person.

string
detail

Structured particulars, when there are any.

object
key
additional properties
remedy

What to do about it, when there is something to do.

string
requestId
required

Quote this when asking about a specific failure.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"detail": {
"additionalProperty": "example"
},
"remedy": "example",
"requestId": "example"
}
}

The request did not match the expected shape.

Media typeapplication/json
object
error
required
object
code
required

A stable identifier for the kind of failure. Safe to branch on.

string
message
required

What went wrong, written for a person.

string
detail

Structured particulars, when there are any.

object
key
additional properties
remedy

What to do about it, when there is something to do.

string
requestId
required

Quote this when asking about a specific failure.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"detail": {
"additionalProperty": "example"
},
"remedy": "example",
"requestId": "example"
}
}

Rate limited. Retry after the interval in the header.

Media typeapplication/json
object
error
required
object
code
required

A stable identifier for the kind of failure. Safe to branch on.

string
message
required

What went wrong, written for a person.

string
detail

Structured particulars, when there are any.

object
key
additional properties
remedy

What to do about it, when there is something to do.

string
requestId
required

Quote this when asking about a specific failure.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"detail": {
"additionalProperty": "example"
},
"remedy": "example",
"requestId": "example"
}
}

GitHub or another dependency refused or did not answer. Nothing in the workspace changed.

Media typeapplication/json
object
error
required
object
code
required

A stable identifier for the kind of failure. Safe to branch on.

string
message
required

What went wrong, written for a person.

string
detail

Structured particulars, when there are any.

object
key
additional properties
remedy

What to do about it, when there is something to do.

string
requestId
required

Quote this when asking about a specific failure.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"detail": {
"additionalProperty": "example"
},
"remedy": "example",
"requestId": "example"
}
}