Skip to content

Start a run: proposals of test cases from pasted text

POST
/v1/projects/{id}/case-generations
curl --request POST \
--url https://beta-api.plune.ai/v1/projects/example/case-generations \
--header 'Content-Type: application/json' \
--cookie plune_session=<plune_session> \
--data '{ "input": { "kind": "pasted-text", "title": "Password reset", "text": "As a shopper I want to reset my password from the sign in page, so that I can get back into my account when I forget it.\nAcceptance criteria:\n- the reset link is sent to the e-mail of the account\n- the link expires after one hour\n- a used link cannot be used again" }, "maxCases": 6, "language": "en" }'

The text (a story, its acceptance criteria) is masked for secrets and put on the queue; the worker sends it to the project’s own AI provider and writes what comes back as proposals in the review queue, each pending, execution: manual, with source {kind: pasted-text}, origin {generationId, kind: ticket, target}, the technique and the run’s cost. Nothing is created in the case tree: a case appears when a reviewer approves a proposal. The text is not stored — the run keeps a hash and counts. Read the run with GET …/case-generations/{generationId}. Answers 202 with the run; the same text within 10 minutes answers 202 with the run it already made, at no new cost — before the monthly limit, the queue’s ceiling and the run limits are asked. Refusals, in order: a reader (403), a token (403 — a person in a session only), the body (400), the ticket mode (424), the text (422), the project’s AI (503/409), the monthly AI limit (429 ai_budget_spent, Retry-After until the 1st, UTC), a full review queue (429 with a quota body, as every quota refusal), proposals or cases from the same text without confirmDuplicates (409 duplicate_source, listed), and the run limits: 3 an hour a member, 20 a day a project, one at a time a project (429 with Retry-After).

id
required
string

An id from GET /v1/projects

Media type application/json
object
input
required
One of:
object
kind
required
string
Allowed values: pasted-text
text
required

The story and its acceptance criteria. Over 12000 characters is a 422 (text_too_long), under 20 words a 422 (ticket_too_empty).

string
title

What the text is called; it becomes the source the proposals name.

string
<= 300 characters
maxCases

The most proposals the run may produce.

integer
default: 8 >= 1 <= 12
language

The language of the cases; the project’s AI language when left out.

string
>= 2 characters <= 35 characters
confirmDuplicates

Proposals or cases from the same text already exist (409 duplicate_source): true generates anyway.

boolean
Examples

Six cases from a story

{
"input": {
"kind": "pasted-text",
"title": "Password reset",
"text": "As a shopper I want to reset my password from the sign in page, so that I can get back into my account when I forget it.\nAcceptance criteria:\n- the reset link is sent to the e-mail of the account\n- the link expires after one hour\n- a used link cannot be used again"
},
"maxCases": 6,
"language": "en"
}

The run, queued — or the one the same text made within 10 minutes

Media type application/json

One run (ADR 0067 point 4). It never holds the text: what went to the model is counted (fetched), masked secrets are counted (redactions), and the text itself lives only in the proposals the run wrote.

object
id
required
string
state
required
string
Allowed values: queued running done failed cancelled
inputKind
required
string
Allowed values: pasted-text jira-ticket
maxCases
required
integer
language
required
string
fetched
required

Characters of each field sent to the model, after masking.

object
title
required
integer
text
required
integer
redactions
required

How many places were masked as a secret before anything was sent.

integer
produced
required

Proposals put in the review queue, each pending.

integer
skipped
required

Proposals left out: invalid broke a bound of the proposal (the field named), over_limit came past maxCases.

Array<object>
object
index
required
integer
reason
required
string
Allowed values: invalid over_limit
field
string
cost
required

Tokens null = the provider reported none; costUsd null beside tokens = the model’s price is unknown — never 0. Counted in the project’s monthly AI limit.

object
inputTokens
required
integer
nullable
outputTokens
required
integer
nullable
costUsd
required
number
nullable
provider

Where the run was sent, once it started.

string
Allowed values: anthropic openrouter custom
model
string
failureKind

Only on failed.

string
Allowed values: key_rejected provider_rate_limited provider_unavailable invalid_response address_rejected timeout ai_unavailable queue_full stalled
requestedBy
required
string
createdAt
required
string format: date-time
startedAt
required
string format: date-time
nullable
finishedAt
required
string format: date-time
nullable
Example
{
"id": "6f1c2b3a-0000-4000-8000-0000000000aa",
"state": "queued",
"inputKind": "pasted-text",
"maxCases": 6,
"language": "en",
"fetched": {
"title": 14,
"text": 214
},
"redactions": 0,
"produced": 0,
"skipped": [],
"cost": {
"inputTokens": null,
"outputTokens": null,
"costUsd": null
},
"requestedBy": "6f1c2b3a-0000-4000-8000-0000000000a2",
"createdAt": "2026-10-25T09:00:00.000Z",
"startedAt": null,
"finishedAt": null
}

Invalid JSON or body

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

No / invalid session

You are in the project as a reader, or a token (a person in a session only)

No such project among yours

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

duplicate_source (with the list), ai_not_connected, ai_disabled, or key_rejected (the saved key cannot be read)

Media type application/json
object
error
required
string
code
string
Allowed values: text_too_long ticket_too_empty jira_not_connected platform_not_configured ai_not_connected ai_disabled key_rejected duplicate_source member_limit project_limit concurrent_limit ai_budget_spent
limit

text_too_long: the most characters a run takes.

integer
minWords

ticket_too_empty: the fewest words a run takes.

integer
duplicates

What the project already holds from the same text: the proposals earlier runs of it put in the queue (any state but approved), and the cases the approved ones became.

object
proposals
required
Array<object>
object
id
required
string
title
required
string
state
required
string
cases
required
Array<object>
object
id
required
string
title
required
string
capUsd

ai_budget_spent: the owner’s monthly limit.

number
spentUsd

ai_budget_spent: what the month has spent, known-price calls and runs only.

number
resetsOn

ai_budget_spent: the day the limit frees (the 1st, UTC).

string format: date
Example
{
"code": "text_too_long"
}

text_too_long / ticket_too_empty

Media type application/json
object
error
required
string
code
string
Allowed values: text_too_long ticket_too_empty jira_not_connected platform_not_configured ai_not_connected ai_disabled key_rejected duplicate_source member_limit project_limit concurrent_limit ai_budget_spent
limit

text_too_long: the most characters a run takes.

integer
minWords

ticket_too_empty: the fewest words a run takes.

integer
duplicates

What the project already holds from the same text: the proposals earlier runs of it put in the queue (any state but approved), and the cases the approved ones became.

object
proposals
required
Array<object>
object
id
required
string
title
required
string
state
required
string
cases
required
Array<object>
object
id
required
string
title
required
string
capUsd

ai_budget_spent: the owner’s monthly limit.

number
spentUsd

ai_budget_spent: what the month has spent, known-price calls and runs only.

number
resetsOn

ai_budget_spent: the day the limit frees (the 1st, UTC).

string format: date
Example
{
"code": "text_too_long"
}

jira_not_connected: the ticket mode is not available

Media type application/json
object
error
required
string
code
string
Allowed values: text_too_long ticket_too_empty jira_not_connected platform_not_configured ai_not_connected ai_disabled key_rejected duplicate_source member_limit project_limit concurrent_limit ai_budget_spent
limit

text_too_long: the most characters a run takes.

integer
minWords

ticket_too_empty: the fewest words a run takes.

integer
duplicates

What the project already holds from the same text: the proposals earlier runs of it put in the queue (any state but approved), and the cases the approved ones became.

object
proposals
required
Array<object>
object
id
required
string
title
required
string
state
required
string
cases
required
Array<object>
object
id
required
string
title
required
string
capUsd

ai_budget_spent: the owner’s monthly limit.

number
spentUsd

ai_budget_spent: what the month has spent, known-price calls and runs only.

number
resetsOn

ai_budget_spent: the day the limit frees (the 1st, UTC).

string format: date
Example
{
"code": "text_too_long"
}

The monthly AI limit (ai_budget_spent) or a run limit (member_limit, project_limit, concurrent_limit), each with Retry-After; or the review queue’s ceiling — a quota body (kind: pendingReviewItems) and no Retry-After, since only working the queue down frees it

Media type application/json
One of:
object
error
required
string
code
string
Allowed values: text_too_long ticket_too_empty jira_not_connected platform_not_configured ai_not_connected ai_disabled key_rejected duplicate_source member_limit project_limit concurrent_limit ai_budget_spent
limit

text_too_long: the most characters a run takes.

integer
minWords

ticket_too_empty: the fewest words a run takes.

integer
duplicates

What the project already holds from the same text: the proposals earlier runs of it put in the queue (any state but approved), and the cases the approved ones became.

object
proposals
required
Array<object>
object
id
required
string
title
required
string
state
required
string
cases
required
Array<object>
object
id
required
string
title
required
string
capUsd

ai_budget_spent: the owner’s monthly limit.

number
spentUsd

ai_budget_spent: what the month has spent, known-price calls and runs only.

number
resetsOn

ai_budget_spent: the day the limit frees (the 1st, UTC).

string format: date
Example
{
"code": "text_too_long"
}
Retry-After
integer

Seconds until the limit lets the next run through: the run window, or the 1st of next month (UTC) for ai_budget_spent

platform_not_configured: the server has no AI encryption key

Media type application/json
object
error
required
string
code
string
Allowed values: text_too_long ticket_too_empty jira_not_connected platform_not_configured ai_not_connected ai_disabled key_rejected duplicate_source member_limit project_limit concurrent_limit ai_budget_spent
limit

text_too_long: the most characters a run takes.

integer
minWords

ticket_too_empty: the fewest words a run takes.

integer
duplicates

What the project already holds from the same text: the proposals earlier runs of it put in the queue (any state but approved), and the cases the approved ones became.

object
proposals
required
Array<object>
object
id
required
string
title
required
string
state
required
string
cases
required
Array<object>
object
id
required
string
title
required
string
capUsd

ai_budget_spent: the owner’s monthly limit.

number
spentUsd

ai_budget_spent: what the month has spent, known-price calls and runs only.

number
resetsOn

ai_budget_spent: the day the limit frees (the 1st, UTC).

string format: date
Example
{
"code": "text_too_long"
}