Skip to content
GitmoapAPIBeta
Planissue-criteria
POST/repositories/{repo_id}/issues/{issue_id}/criteria

Add an acceptance criterion to a work item's Definition of Done

create_issue_criterion
  • issue:update
    A token needs the issue:update scope: Update work items — status, fields, hierarchyGranted by mcp_agent_write, write_api, write_repository.Every operation this scope unlocks
  • Audited · mutation

    x-gitmoap-audit: {"kind":"audited","arg":"mutation"}

  • Quota · criteria per issue

    x-gitmoap-quota: {"kind":"enforced","arg":"criteria_per_issue"}

Adds a criterion with its text, and optionally a binding — a test, metric, coverage floor or pipeline node whose result decides it. An unbound criterion is a manual checkbox; a bound one starts unverified and cannot be created already ticked. It goes last unless you give a position, and is required to close unless you say otherwise. Developers and above may add criteria, up to the per-work-item and per-repository limits.

Parameters

Path
  • repo_idRepositoryId (string · uuid)Repository ID
  • issue_idIssueId (integer · int64)Work-item iid

Request body

application/json
  • textstringThe human statement — the criterion's title.
  • bindingCriterionBinding or nulloptionalThe typed binding. Absent (or null) is a manual checkbox.
  • checkedboolean or nulloptionalFor a manual criterion, whether it starts ticked. Must be false or absent for a bound one.
  • descriptionstring or nulloptional
  • ordinalinteger · int32 or nulloptional0–∞Where to place it; appended to the end when absent.
  • required_for_closeboolean or nulloptionalWhether this criterion must be satisfied before the work item can be closed or verified. Defaults to true; set false for an aspirational one.
Request body exampleSynthesized example
{
"text": "text-962d"
}

Responses

200The created criterionapplication/json
  • created_atstring
  • criterion_idCriterionId (string · uuid)The criterion's identifier. It never changes, so rewording or re-binding a criterion keeps its identity and its evidence.
  • issue_uidIssueUid (string · uuid)
  • satisfiedboolean
  • stateCriterionStateunverifiedpassingfailingstalemanualA criterion's verdict.
  • textstringThe human statement — the criterion's title. The label, not the identity.
  • updated_atstring
  • bindingCriterionBinding or nulloptional
  • binding_exprstring or nulloptionalThe binding's @-expression, as written. Absent for a manual criterion.
  • binding_keystring or nulloptionalThe normalised form of binding_expr, which two equivalent spellings share. Absent for a manual criterion.
  • checkedbooleanoptionalWhether the box is ticked. Meaningful only for a manual criterion — a bound one is ticked by its state, and ticking it by hand is refused with a 409.
  • descriptionstring or nulloptionalAn optional longer description (markdown) — the per-criterion detail a flat checkbox could never hold.
  • evidenceEvidence or nulloptional
    • The Evidence schema
    • commit_shaCommitHash (string)
    • observed_atstringWhen the run that produced the evidence finished, RFC 3339.
    • pipeline_idPipelineId (string · uuid)
    • pipeline_run_idPipelineRunId (string · uuid)
    • node_run_idNodeRunId (string · uuid) or nulloptional
    • valuenumber · double or nulloptional
  • facet_categoryFacetCategory (string) or nulloptional
  • facet_keyFacetKey (string) or nulloptionalThe blueprint facet this criterion belongs to, when a blueprint added it. Absent for a criterion written directly. A facet's state is rolled up from the criteria carrying its key.
  • last_evidence_atstring or nulloptional
  • last_evidence_commit_shaCommitHash (string) or nulloptional
  • last_evidence_node_run_idNodeRunId (string · uuid) or nulloptional
  • last_evidence_pipeline_idPipelineId (string · uuid) or nulloptionalThe pipeline whose run last provided evidence for this criterion. Only a later run of the same pipeline can make the criterion stale.
  • last_evidence_run_idPipelineRunId (string · uuid) or nulloptional
  • last_evidence_valuenumber · double or nulloptionalThe observed value, for a Metric or Coverage binding.
  • ordinalinteger · int32optional0–∞Position in the description, so the list round-trips in authoring order.
  • required_for_closebooleanoptional@optional opts out. Everything else in a Definition of Done is required by definition.
200 exampleSynthesized example
{
"created_at": "2026-10-13T22:27:32Z",
"criterion_id": "d5789762-dd6d-47f7-a865-e008d1283342",
"issue_uid": "abab5ae3-7a9b-47a4-b9f0-48e6bd8afe0b",
"satisfied": false,
"state": "unverified",
"text": "text-eaee",
"updated_at": "2026-10-12T12:54:55Z",
"binding": {
"kind": "test",
"test_id": "fd4ee03f-93f3-402d-a425-1a1e65a4b82d",
"suite": "suite-d008"
},
"binding_expr": "binding-expr-a484",
"binding_key": "binding-key-ed3e",
"checked": false,
"description": "Renders each region as density contours of its operations.",
"evidence": {
"commit_sha": "ccda5159578950281f615117fda3777980ac0662",
"observed_at": "2026-10-13T06:00:27Z",
"pipeline_id": "a1e755f5-c3eb-4630-90e1-0494ef782ea5",
"pipeline_run_id": "9e2b7c41-3d5a-4f8e-b1c2-7a6d5e4f3c2b",
"node_run_id": "085cf3f6-9dd4-4b7f-82c5-bc6ddb52d1d9",
"value": 22
},
"facet_category": "facet-category-1643",
"facet_key": "facet-key-6ff0",
"last_evidence_at": "2026-10-13T12:11:25Z",
"last_evidence_commit_sha": "0e020e1cf86c21469c63b93e095ce32c47769c12",
"last_evidence_node_run_id": "3c4c933a-ca7c-4584-a80c-8745f8a73858",
"last_evidence_pipeline_id": "7f0d0251-1759-4068-91aa-c59ebfcb8f58",
"last_evidence_run_id": "9e2b7c41-3d5a-4f8e-b1c2-7a6d5e4f3c2b",
"last_evidence_value": 69,
"ordinal": 70,
"required_for_close": false
}
400Invalid text or bindingapplication/json
Error codes
  • BAD_REQUESTThe request is malformed: a parameter or a body field is not a well-formed value of its type
  • INVALID_BINDINGThe criterion binding is not well-formed
  • errorErrorBody
    • The ErrorBody schema
    • codestringThe coarse category, tied to the status: NOT_FOUND, CONFLICT, INSUFFICIENT_SCOPE. Every error has one; the API reference's error catalogue lists them all.
    • messagestringWhat went wrong, for a person. Written to be read, not parsed: match on reason or code.
    • fieldstring or nulloptionalOn a 400 that refused one field's value, the field, as its JSON path: email, items[0].name. Put the message under that input. Absent on every other error.
    • reasonstring or nulloptionalThe specific code, when the error has one: LAST_ORGANIZATION_OWNER, BRANCH_PROTECTED. The message starts with it too. Match on this when it is present, and on code otherwise.
    • required_scopestring or nulloptionalThe scope the access token was missing, on INSUFFICIENT_SCOPE.
    • violationsanyoptionalPresent on the structured-validation errors (PROVIDER_VALIDATION_FAILED per-node, PIPELINE_VALIDATION_FAILED DAG): a JSON array of the offending elements, so a caller can fix every one in a single round-trip. Absent (omitted) on every other error.
400 exampleSynthesized example
{
"error": {
"code": "code-0d3e",
"message": "Pin a page's payload to the build that rendered it"
}
}
401Unauthenticated — no credential, or an invalid oneapplication/json
Error codes
  • UNAUTHORIZEDNo credential was sent, or the one sent is invalid, expired or revoked
  • errorErrorBody
    • The ErrorBody schema
    • codestringThe coarse category, tied to the status: NOT_FOUND, CONFLICT, INSUFFICIENT_SCOPE. Every error has one; the API reference's error catalogue lists them all.
    • messagestringWhat went wrong, for a person. Written to be read, not parsed: match on reason or code.
    • fieldstring or nulloptionalOn a 400 that refused one field's value, the field, as its JSON path: email, items[0].name. Put the message under that input. Absent on every other error.
    • reasonstring or nulloptionalThe specific code, when the error has one: LAST_ORGANIZATION_OWNER, BRANCH_PROTECTED. The message starts with it too. Match on this when it is present, and on code otherwise.
    • required_scopestring or nulloptionalThe scope the access token was missing, on INSUFFICIENT_SCOPE.
    • violationsanyoptionalPresent on the structured-validation errors (PROVIDER_VALIDATION_FAILED per-node, PIPELINE_VALIDATION_FAILED DAG): a JSON array of the offending elements, so a caller can fix every one in a single round-trip. Absent (omitted) on every other error.
401 exampleSynthesized example
{
"error": {
"code": "code-5cce",
"message": "Label every synthesized example"
}
}
403Forbidden — the caller's access level or token scope is insufficientapplication/json
Error codes
  • FORBIDDENYou are signed in but may not do this here; reason names the permission or the rule that refused it
  • INSUFFICIENT_SCOPEThe access token lacks the scope this operation needs; required_scope names it
  • PERMISSION_REQUIREDYou need a permission you do not hold here; the message names it
  • errorErrorBody
    • The ErrorBody schema
    • codestringThe coarse category, tied to the status: NOT_FOUND, CONFLICT, INSUFFICIENT_SCOPE. Every error has one; the API reference's error catalogue lists them all.
    • messagestringWhat went wrong, for a person. Written to be read, not parsed: match on reason or code.
    • fieldstring or nulloptionalOn a 400 that refused one field's value, the field, as its JSON path: email, items[0].name. Put the message under that input. Absent on every other error.
    • reasonstring or nulloptionalThe specific code, when the error has one: LAST_ORGANIZATION_OWNER, BRANCH_PROTECTED. The message starts with it too. Match on this when it is present, and on code otherwise.
    • required_scopestring or nulloptionalThe scope the access token was missing, on INSUFFICIENT_SCOPE.
    • violationsanyoptionalPresent on the structured-validation errors (PROVIDER_VALIDATION_FAILED per-node, PIPELINE_VALIDATION_FAILED DAG): a JSON array of the offending elements, so a caller can fix every one in a single round-trip. Absent (omitted) on every other error.
403 exampleSynthesized example
{
"error": {
"code": "code-638a",
"message": "Inline scalar newtypes but keep their brand"
}
}
404Work item not foundapplication/json
Error codes
  • NOT_FOUNDThe resource does not exist — or exists and you cannot see it: a private resource you have no access to answers exactly as an absent one does
  • errorErrorBody
    • The ErrorBody schema
    • codestringThe coarse category, tied to the status: NOT_FOUND, CONFLICT, INSUFFICIENT_SCOPE. Every error has one; the API reference's error catalogue lists them all.
    • messagestringWhat went wrong, for a person. Written to be read, not parsed: match on reason or code.
    • fieldstring or nulloptionalOn a 400 that refused one field's value, the field, as its JSON path: email, items[0].name. Put the message under that input. Absent on every other error.
    • reasonstring or nulloptionalThe specific code, when the error has one: LAST_ORGANIZATION_OWNER, BRANCH_PROTECTED. The message starts with it too. Match on this when it is present, and on code otherwise.
    • required_scopestring or nulloptionalThe scope the access token was missing, on INSUFFICIENT_SCOPE.
    • violationsanyoptionalPresent on the structured-validation errors (PROVIDER_VALIDATION_FAILED per-node, PIPELINE_VALIDATION_FAILED DAG): a JSON array of the offending elements, so a caller can fix every one in a single round-trip. Absent (omitted) on every other error.
404 exampleSynthesized example
{
"error": {
"code": "code-7905",
"message": "Stream pipeline status into the run header"
}
}
409A bound criterion cannot be created already tickedapplication/json
Error codes
  • CONFLICTThe request conflicts with the resource's current state; reason says how
  • CRITERION_IS_BOUNDThe criterion is bound to evidence a pipeline produces, so it cannot be ticked by hand; unbind it first
  • LEGAL_ACCEPTANCE_REQUIREDAccept the current Terms of Service before changing anything; reading stays open
  • errorErrorBody
    • The ErrorBody schema
    • codestringThe coarse category, tied to the status: NOT_FOUND, CONFLICT, INSUFFICIENT_SCOPE. Every error has one; the API reference's error catalogue lists them all.
    • messagestringWhat went wrong, for a person. Written to be read, not parsed: match on reason or code.
    • fieldstring or nulloptionalOn a 400 that refused one field's value, the field, as its JSON path: email, items[0].name. Put the message under that input. Absent on every other error.
    • reasonstring or nulloptionalThe specific code, when the error has one: LAST_ORGANIZATION_OWNER, BRANCH_PROTECTED. The message starts with it too. Match on this when it is present, and on code otherwise.
    • required_scopestring or nulloptionalThe scope the access token was missing, on INSUFFICIENT_SCOPE.
    • violationsanyoptionalPresent on the structured-validation errors (PROVIDER_VALIDATION_FAILED per-node, PIPELINE_VALIDATION_FAILED DAG): a JSON array of the offending elements, so a caller can fix every one in a single round-trip. Absent (omitted) on every other error.
409 exampleSynthesized example
{
"error": {
"code": "code-a0e0",
"message": "Stream pipeline status into the run header"
}
}
429Per-issue or per-repository criteria limit reachedapplication/json
Error codes
  • TOO_MANY_REQUESTSRetryableA quota, a rate limit or a transfer budget is spent. When the response carries Retry-After, retry after that many seconds
  • errorErrorBody
    • The ErrorBody schema
    • codestringThe coarse category, tied to the status: NOT_FOUND, CONFLICT, INSUFFICIENT_SCOPE. Every error has one; the API reference's error catalogue lists them all.
    • messagestringWhat went wrong, for a person. Written to be read, not parsed: match on reason or code.
    • fieldstring or nulloptionalOn a 400 that refused one field's value, the field, as its JSON path: email, items[0].name. Put the message under that input. Absent on every other error.
    • reasonstring or nulloptionalThe specific code, when the error has one: LAST_ORGANIZATION_OWNER, BRANCH_PROTECTED. The message starts with it too. Match on this when it is present, and on code otherwise.
    • required_scopestring or nulloptionalThe scope the access token was missing, on INSUFFICIENT_SCOPE.
    • violationsanyoptionalPresent on the structured-validation errors (PROVIDER_VALIDATION_FAILED per-node, PIPELINE_VALIDATION_FAILED DAG): a JSON array of the offending elements, so a caller can fix every one in a single round-trip. Absent (omitted) on every other error.
429 exampleSynthesized example
{
"error": {
"code": "code-6466",
"message": "Inline scalar newtypes but keep their brand"
}
}

CLI

gitmoap issue-criteria create

  • --repo-ididRepository ID (id or owner/repo path)
  • --issue-ididWork-item iid
  • --bindingjsonoptional
  • --checkedflagoptionalFor a manual criterion, whether it starts ticked. Must be `false` or absent for a bound one.
  • --descriptionstringoptional
  • --ordinalnumberoptionalWhere to place it; appended to the end when absent.
  • --required-for-closeflagoptionalWhether this criterion must be satisfied before the work item can be closed or verified. Defaults to `true`; set `false` for an aspirational one.
  • --textstringThe human statement — the criterion's title.

Used in these guides