Eval Results

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

Requires the 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.
  • score must be in [0, 1] (400, error_code: "invalid_score" otherwise).

Submit a Verdict

POST
/api/v1/eval-results

Submit 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".

FieldTypeRequiredDescription
subject_typestringyes"skill" or "log"
subject_idstring | numberyesUUID string (skill) or int64 (log)
sourcestringyesEvaluator name, see source rules above
scorenumberno0.01.0
passedboolnoVerdict
axesobjectnoSkill subject only, per-axis {score, rationale}; defaults to a single "external" axis built from score/external_ref if omitted
external_refstringnoOpaque pointer into the external system (e.g. a promptfoo run ID)
detailsobjectnoLog subject only, arbitrary JSON, defaults to {}

Skill Subject

bash
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"
  }'
jsonResponse (201 Created)
{
  "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

bash
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"}
  }'
jsonResponse (201 Created)
{
  "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>"}.

Statuserror_codeCause
400invalid_requestMalformed JSON body
400invalid_sourcesource fails the source rules above
400invalid_scorescore outside [0, 1]
400invalid_subjectUnknown subject_type, or subject_id doesn't parse for the given type
404not_foundsubject_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.
500internalInternal store error

List Verdicts for a Log Row

GET
/api/v1/eval-results

List verdicts recorded against a log row. Skill evaluations are listed via GET /api/v1/skills/:id/evaluations instead.

bash
curl "https://api.ingateai.com/api/v1/eval-results?log_id=42&limit=20" \
  -H "X-Ingate-Key: sk-ingate-..."
ParamTypeRequiredDescription
log_idintyesLog row ID
limitintnoMax results
jsonResponse (200 OK)
{"results": [ /* LogEvalResult, ... */ ]}
Statuserror_codeCause
400missing_log_idlog_id query param absent
400invalid_log_idlog_id present but not an integer
500internalInternal 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:

bash
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.