Skip to content

Test and connect GitHub Actions, GitLab CI or CircleCI, or change the connection

PUT
/v1/projects/{id}/integrations/ci
curl --request PUT \
--url https://beta-api.plune.ai/v1/projects/example/integrations/ci \
--header 'Content-Type: application/json' \
--cookie plune_session=<plune_session> \
--data '{ "provider": "github", "repository": "plune-ai/cli", "workflow": ".github/workflows/plune.yml", "ref": "main", "token": "github_pat_EXAMPLE_not_a_real_token_0000000000000000000000" }'

Owner only. Runs the five checks of POST …/test with what was sent, and saves the connection only if every one passed — the token encrypted with the server’s key, bound to this project, never returned — answering with what GET shows, the check as its last one. A check that failed saves nothing and answers 422 with the checks. One CI a project for now: with another one connected, 409 and nothing is sent. Left out of an edit of a project already connected, token is the saved one, and so are who saved it and when. Recorded in the audit feed of the project as a ci_integration entry, every token… field masked. At most 10 checks a minute for a project, this and POST …/test together, counted after the role. Session cookie only.

id
required
string

An id from GET /v1/projects

Media type application/json
One of: discriminator: provider

GitHub Actions on one repository.

object
provider
required
string
Allowed values: github
repository
required

owner/name, as in the address of the repository on GitHub.

string
/^[A-Za-z0-9][A-Za-z0-9-]{0,38}/[A-Za-z0-9._-]{1,100}$/
workflow
required

The workflow file, .github/workflows/<name>.yml (or .yaml).

string
/^\.github/workflows/[A-Za-z0-9._-]+\.ya?ml$/
ref
required

The branch or tag launches run on.

string
>= 1 characters <= 255 characters
token

Write-only: a fine-grained personal access token on this one repository with one permission, Repository permissions → Actions → Read and write. A classic token (ghp_) is refused. Stored encrypted, bound to the project, and in no response, no log and no error message. Required for a new connection; left out of an edit, the saved one is kept.

string
/^github_pat_[A-Za-z0-9_]{22,255}$/
Example
{
"provider": "github",
"repository": "plune-ai/cli",
"workflow": ".github/workflows/plune.yml",
"ref": "main",
"token": "github_pat_EXAMPLE_not_a_real_token_0000000000000000000000"
}

Connected — the same body as GET

Media type application/json

The CI of a project, as a member of it reads it. With none connected it is { "configured": false }. The token is not in it.

object
configured
required
boolean
provider

Present exactly when configured is true, as every field below but url and the four token….

string
Allowed values: github gitlab circleci
url

GitLab only: its address.

string
repository

GitHub’s owner/name, GitLab’s group/project, CircleCI’s project slug.

string
workflow
string
ref
string
savedBy

userId of the owner who connected it, or last changed it.

string
savedAt
string format: date-time
verifiedAt

When the saved connection was last checked: the save itself, or POST …/test.

string format: date-time
nullable
verifyResult
object
ok
required
boolean
steps
required
Array<object>
object
step
required
string
Allowed values: token repository actions workflow inputs
result
required
string
Allowed values: ok failed skipped
code

Present exactly when result is failed.

string
Allowed values: unreachable rate_limited token_rejected repository_not_found actions_denied workflow_not_found workflow_disabled ref_not_found no_workflow_dispatch inputs_missing variables_denied address_refused unexpected
status

The HTTP status the CI answered with, for unexpected. Never its body.

integer
missing

inputs_missing only: the inputs the workflow does not take.

Array<string>
Allowed values: plune_run grep ref
tokenSavedBy

Owner only: who saved the token. An edit that keeps the token keeps this.

string
tokenSavedAt

Owner only.

string format: date-time
tokenExpiresAt

Owner only: when the CI says the token expires, as of its last check; null when it does not, or the CI did not say.

string format: date-time
nullable
tokenOwner

Owner only: whose token it is, when the CI says — the CircleCI user of a personal API token; null otherwise.

string
nullable
Example
{
"configured": true,
"provider": "github",
"repository": "plune-ai/cli",
"workflow": ".github/workflows/plune.yml",
"ref": "main",
"savedBy": "u-shop",
"savedAt": "2026-09-16T09:12:04.151Z",
"verifiedAt": "2026-09-16T09:12:04.151Z",
"verifyResult": {
"ok": true,
"steps": [
{
"step": "token",
"result": "ok"
},
{
"step": "repository",
"result": "ok"
},
{
"step": "actions",
"result": "ok"
},
{
"step": "workflow",
"result": "ok"
},
{
"step": "inputs",
"result": "ok"
}
]
},
"tokenSavedBy": "u-shop",
"tokenSavedAt": "2026-09-16T09:12:04.151Z",
"tokenExpiresAt": null
}

Invalid body, or a first connect with no token; the message names the field and never a value. Nothing was sent to GitHub

Media type application/json
object
error
required
string
Examples

A first connect with no token

{
"error": "validation failed — token: is required for a new connection"
}

No / invalid session — a CLI token is refused here

You are in the project, but not as its owner

Media type application/json
object
error
required
string
Example
{
"error": "project owner only"
}

No such project among yours

Media type application/json
object
error
required
string
Example
{
"error": "project 'u-shop::default' not found"
}

Another CI is connected — one CI a project for now: disconnect it first. Nothing was sent to the CI

Media type application/json
object
error
required
string
Example
{
"error": "GitHub Actions is connected — one CI a project for now: disconnect it to use GitLab CI"
}

A check failed; nothing was saved. The body is the check, as POST …/test answers it, with error. A failed check carries one of these codes: unreachable — the CI did not answer in time; rate_limited — the CI is limiting the token’s requests: try again in a minute; token_rejected — the CI does not accept the token: it is mistyped, expired or revoked; repository_not_found — the token does not reach the repository, project or project slug: a private one it was not given, or a name that is not there; actions_denied — the token may not start workflows or pipelines: on GitHub, Repository permissions → Actions → Read and write; on GitLab, role Developer and scope api; on CircleCI, a user who may start the project’s pipelines; workflow_not_found — the workflow file, .gitlab-ci.yml or .circleci/config.yml is not there; workflow_disabled — the workflow is switched off on GitHub; ref_not_found — the branch or tag is not there; no_workflow_dispatch — the GitHub workflow does not run on workflow_dispatch; inputs_missing — the workflow, job or config does not take everything Plune sends (GitHub: the inputs plune_run, grep, ref; GitLab: a job for $PLUNE_RUN that greps by $PLUNE_GREP; CircleCI: the parameters plune_run and grep): missing names them; variables_denied — the GitLab project keeps pipeline variables from the token’s role: Settings → CI/CD → Variables → Minimum role to use pipeline variables → Developer; address_refused — the GitLab address is not https, is an IP address, or is on a private network: Plune sends no request there; unexpected — the CI answered with a status the check did not expect: the step carries it.

Media type application/json
object
ok
required
boolean
steps
required
Array<object>
object
step
required
string
Allowed values: token repository actions workflow inputs
result
required
string
Allowed values: ok failed skipped
code

Present exactly when result is failed.

string
Allowed values: unreachable rate_limited token_rejected repository_not_found actions_denied workflow_not_found workflow_disabled ref_not_found no_workflow_dispatch inputs_missing variables_denied address_refused unexpected
status

The HTTP status the CI answered with, for unexpected. Never its body.

integer
missing

inputs_missing only: the inputs the workflow does not take.

Array<string>
Allowed values: plune_run grep ref
testedAt
required
string format: date-time
error
required
string
Example
{
"error": "the connection did not pass its checks — nothing was saved",
"ok": false,
"steps": [
{
"step": "token",
"result": "ok"
},
{
"step": "repository",
"result": "ok"
},
{
"step": "actions",
"result": "failed",
"code": "actions_denied"
},
{
"step": "workflow",
"result": "skipped"
},
{
"step": "inputs",
"result": "skipped"
}
],
"testedAt": "2026-09-16T09:12:04.151Z"
}

More than 60 saves a minute from you, or more than 10 checks a minute for this project. Retry-After carries the seconds until the window rolls over

Media type application/json
object
error
required
string
Example generated
{
"error": "example"
}
Retry-After
integer

Seconds until the window rolls over

The server has no key to keep tokens under; nothing is saved

Media type application/json
object
error
required
string
Example
{
"error": "integrations are not configured on this server — contact your operator"
}