Eval Results API
Submit pass/fail verdicts from third-party evaluators into Ingate.
The eval-results ingest API lets a third-party evaluator (promptfoo, a CI judge, a custom script) submit a pass/fail verdict into Ingate, against either a skill (landing as a source-tagged skill.Evaluation, visible alongside Ingate's own judge runs) or a proxied log row (landing in the log_eval_results table).
Entitlement and roles
integrations entitlement (Enterprise plan). Reads need the viewer role; writes need member.Source Rules
Every submission carries a source string identifying the evaluator that produced it (e.g. "promptfoo", "ci-judge"):
- Must match
^[a-z0-9_-]{1,32}$, lowercase letters, digits,_,-, 1–32 chars. "internal"is reserved for Ingate's own built-in skill judge and is rejected (400,error_code: "invalid_source") from this endpoint.scoremust be in[0, 1](400,error_code: "invalid_score"otherwise).
Submit a Verdict
/api/v1/eval-resultsSubmit an external verdict against a skill or a log row.
subject_id's JSON shape depends on subject_type: a string (UUID) for "skill", a number (log ID) for "log".
| Field | Type | Required | Description |
|---|---|---|---|
subject_type | string | yes | "skill" or "log" |
subject_id | string | number | yes | UUID string (skill) or int64 (log) |
source | string | yes | Evaluator name, see source rules above |
score | number | no | 0.0–1.0 |
passed | bool | no | Verdict |
axes | object | no | Skill subject only, per-axis {score, rationale}; defaults to a single "external" axis built from score/external_ref if omitted |
external_ref | string | no | Opaque pointer into the external system (e.g. a promptfoo run ID) |
details | object | no | Log subject only, arbitrary JSON, defaults to {} |
Skill Subject
curl -X POST https://api.ingateai.com/api/v1/eval-results \
-H "X-Ingate-Key: sk-ingate-..." \
-H "Content-Type: application/json" \
-d '{
"subject_type": "skill",
"subject_id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"source": "promptfoo",
"score": 0.92,
"passed": true,
"external_ref": "promptfoo-run-4471"
}'{
"id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"org_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"skill_id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"skill_version": 3,
"axes": {"external": {"score": 0.92, "rationale": "promptfoo-run-4471"}},
"overall_score": 0.92,
"passed": true,
"judge_provider": "promptfoo",
"judge_model": "external",
"latency_ms": 0,
"prompt_tokens": 0,
"completion_tokens": 0,
"source": "promptfoo",
"external_ref": "promptfoo-run-4471",
"created_at": "2026-08-29T12:00:00Z"
}The evaluation is recorded against the skill's current version at submission time. See the publish-gate interplay section below for how this feeds POST /api/v1/skills/:id/publish.
Log Subject
curl -X POST https://api.ingateai.com/api/v1/eval-results \
-H "X-Ingate-Key: sk-ingate-..." \
-H "Content-Type: application/json" \
-d '{
"subject_type": "log",
"subject_id": 42,
"source": "ci-judge",
"score": 1.0,
"passed": true,
"details": {"rubric": "tone-check", "notes": "matches brand voice"}
}'{
"id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"org_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"log_id": 42,
"source": "ci-judge",
"score": 1.0,
"passed": true,
"details": {"rubric": "tone-check", "notes": "matches brand voice"},
"created_at": "2026-08-29T12:00:00Z"
}Errors (both subject types)
Every error response has the shape {"error": "<message>", "error_code": "<code>"}.
| Status | error_code | Cause |
|---|---|---|
400 | invalid_request | Malformed JSON body |
400 | invalid_source | source fails the source rules above |
400 | invalid_score | score outside [0, 1] |
400 | invalid_subject | Unknown subject_type, or subject_id doesn't parse for the given type |
404 | not_found | subject_type: "skill", no skill with that ID in the caller's org. subject_type: "log", no log row with that ID, or the log belongs to a different org. Both cases return the byte-identical body, no cross-org distinguishability. |
500 | internal | Internal store error |
List Verdicts for a Log Row
/api/v1/eval-resultsList verdicts recorded against a log row. Skill evaluations are listed via GET /api/v1/skills/:id/evaluations instead.
curl "https://api.ingateai.com/api/v1/eval-results?log_id=42&limit=20" \
-H "X-Ingate-Key: sk-ingate-..."| Param | Type | Required | Description |
|---|---|---|---|
log_id | int | yes | Log row ID |
limit | int | no | Max results |
{"results": [ /* LogEvalResult, ... */ ]}| Status | error_code | Cause |
|---|---|---|
400 | missing_log_id | log_id query param absent |
400 | invalid_log_id | log_id present but not an integer |
500 | internal | Internal store error |
Publish-Gate Interplay
A skill's rubric config (GET /api/v1/skills/rubric) has an allowed_verdict_sources list, the Evaluation.Source values the marketplace publish gate accepts as evidence of a passing evaluation. It defaults to ["internal"] (Ingate's own judge only).
To let an external evaluator gate publishing, add its source name to that list:
curl -X PUT https://api.ingateai.com/api/v1/skills/rubric/<rubric-id> \
-H "X-Ingate-Key: sk-ingate-..." \
-H "Content-Type: application/json" \
-d '{"allowed_verdict_sources": ["internal", "promptfoo"]}'POST /api/v1/skills/:id/publish then looks up the latest evaluation for the skill's current version from any of those sources and requires it to have passed: true. See the Skills API for the full gate and its 409 not_publishable response, and the Promptfoo integration for an end-to-end example.