API reference
REST JSON API and MCP server. Authenticate with a bearer key in the Authorization header: create one under Settings → API once you've made an organization. Swap your-org-slug-goes-here and YOUR_TOKEN below for your own.
MCP Server
Connect Claude Code, Cursor, or any MCP client to manage prompts, runs, datasets, and metrics conversationally. 54 tools over streamable HTTP.
claude mcp add --transport http completion-kit \ https://completionkit.com/orgs/your-org-slug-goes-here/mcp \ --header "Authorization: Bearer YOUR_TOKEN"
{
"mcpServers": {
"completion-kit": {
"url": "https://completionkit.com/orgs/your-org-slug-goes-here/mcp",
"headers": {
"Authorization": "Bearer YOUR_TOKEN"
}
}
}
}
Available tools
prompts 7
prompts_list
List all prompts
prompts_get
Get a prompt by ID
prompts_create
Create a prompt
prompts_update
Update a prompt. If the prompt already has runs, this creates a new DRAFT version (current=false) rather than editing in place or publishing — promote it with prompts_publish — so an agent's edits don't go live without a gate. If it has no runs, it is updated in place.
prompts_delete
Delete a prompt
prompts_publish
Publish a prompt version, making it the current version
prompts_suggest_improvement
Suggest an improved version of a prompt, grounded in a run's test results and judge feedback. Analyzes the run's responses, scores, and reviews, then returns reasoning plus a rewritten template (preserving {{variables}}) and persists it as a Suggestion. Requires a run that has a prompt (not a scoring-only run).
runs 9
runs_list
List all runs
runs_get
Get a run by ID, including "metric_averages": a per-metric breakdown with each metric's average score (or pass rate for checks), how many rows it graded, and how many scored low. Use this to find the metric dragging a prompt down without listing responses.
runs_create
Create a run. Omit prompt_id and provide output_column to score existing outputs by grading a pre-existing dataset column instead of generating new ones.
runs_update
Update a run
runs_delete
Delete a run
runs_generate
Start a run. Required for every run, including score-only runs (no prompt): generates responses with the prompt when there is one, otherwise copies the graded dataset column and grades it.
runs_regrade
Re-grade a run's existing responses with its currently attached metrics, without regenerating. Use after attaching or editing metrics on an already-generated run.
runs_rerun
Create and start a fresh copy of a run with the same prompt, dataset, metrics, and settings. Use when the judge changed and you want a clean run instead of mixing versions.
runs_retry_failures
Re-run only the failed responses of a run, optionally limited to specific response ids via "only".
responses 2
responses_list
List responses for a run, in row order. Returns {total, limit, offset, returned, responses}. Defaults to 50 rows because full payloads are large: use "fields" to drop the bodies, "min_score"/"max_score" to isolate low scorers, and sort "score_asc" to read the worst rows first. For per-metric averages of the whole run use runs_get instead of aggregating here.
responses_get
Get a specific response
datasets 6
datasets_list
List all datasets
datasets_get
Get a dataset by ID
datasets_create
Create a dataset with CSV data. First row is the header. Two column names are recognized specially: "expected_output" is each row's answer key (ground truth) given to the judge and to checks that compare against the row's expected value, and "actual_output" is a pre-made output to score in a prompt-less run. Both are overridable per run (expected_column / output_column). Every column is also available to the prompt as a variable.
datasets_update
Update a dataset
datasets_delete
Delete a dataset
datasets_create_from_url
Create a dataset by downloading CSV from a URL instead of inlining it. Use this for large datasets: pass a public http(s) URL and the server fetches the CSV directly, so the data never has to pass through the tool-call arguments. The URL is SSRF-checked and the download is capped at 10MB. First row is the header; the "expected_output" (answer key) and "actual_output" (pre-made output) columns are recognized specially, overridable per run.
metrics 6
metrics_list
List all metrics
metrics_get
Get a metric by ID
metrics_create
Create a metric with evaluation criteria. For a deterministic check set metric_type:"check" and check_config. Per-kind required keys: value (contains/not_contains/equals), pattern (regex), json_path+expected (json_path_equals), min and/or max (length_bounds); valid_json takes no extra keys. target_path is required when target is json_path. For contains, not_contains, and equals, set compare_to:"expected" to grade against each row's own expected_output (ground truth) instead of a constant value (drop value); add expected_path to dig into the expected value when it is JSON.
metrics_update
Update a metric. For a deterministic check set metric_type:"check" and check_config. Per-kind required keys: value (contains/not_contains/equals), pattern (regex), json_path+expected (json_path_equals), min and/or max (length_bounds); valid_json takes no extra keys. target_path is required when target is json_path. For contains, not_contains, and equals, set compare_to:"expected" to grade against each row's own expected_output (ground truth) instead of a constant value (drop value); add expected_path to dig into the expected value when it is JSON.
metrics_delete
Delete a metric
metrics_suggest_variants
Ask the model to rewrite the metric's judge instruction in N variants targeted at the recent disagreements. Each variant is saved as a draft MetricVersion with source="suggestion". Returns the persisted drafts. Stripe-metering hooks fire via ActiveSupport::Notifications under completion_kit.judge_suggestion.generated.
metric 8
metric_groups_list
List all metric groups
metric_groups_get
Get a metric group by ID
metric_groups_create
Create a metric group
metric_groups_update
Update a metric group
metric_groups_delete
Delete a metric group
metric_versions_list
List every MetricVersion (drafts + published) for a metric, newest first. Each row carries version_number, state, source, current flag, and timestamps.
metric_versions_publish
Publish a MetricVersion as the live version of its metric. Works for both 'draft → published' and 'revert to an older published version → current'. Transactionally flips current, demotes peers, and writes the version's instruction + rubric_bands back onto the metric so the judge grades against it.
metric_versions_dismiss
Destroy a draft MetricVersion (use for either source: 'edit' or source: 'suggestion'). Published versions are refused — to demote a published version, publish a different one as current instead.
provider 5
provider_credentials_list
List all provider credentials (API keys are not exposed)
provider_credentials_get
Get a provider credential by ID (API key is not exposed)
provider_credentials_create
Create a provider credential
provider_credentials_update
Update a provider credential
provider_credentials_delete
Delete a provider credential
tags 5
tags_list
List all tags
tags_get
Get a tag by ID
tags_create
Create a tag. Color is auto-assigned.
tags_update
Rename a tag.
tags_delete
Delete a tag. Removes the tag from every linked metric, prompt, run, and dataset.
agreements 2
agreements_list
List agreements. Filter by run_id, response_id, metric_id, or created_by.
agreements_create
Upsert an agreement for (run, response, metric, created_by). Verdict is one of agree, disagree, borderline. corrected_score (1..5) is required when verdict is 'disagree'.
judges 2
judges_replay
Create a scoring run for the current judge over a dataset's existing outputs (wraps runs_create with prompt_id omitted and output_column supplied). This only sets up the run; call runs_generate to actually re-judge the outputs so you can compare against human verdicts.
judges_compare
Compare two versions of one metric's agreement stats side by side. Requires metric_id, metric_version_a_id, and metric_version_b_id (both versions must belong to that metric). Unavailable for check metrics.
promptfoo 1
promptfoo_import
Import a promptfooconfig.yaml. Creates a prompt, a dataset from the test vars, and metrics from the assert blocks (llm-rubric/g-eval become judge metrics; contains/equals/regex/is-json become deterministic check metrics). Returns a summary of what mapped and what was skipped and why; nothing is dropped silently.
usage 1
usage_get
Get this organization's plan usage and limits for the current billing period: runs and prompt fetches used, their limits, how many remain, and when the period resets. Call this to pre-check quota before starting runs. Runs are hard-blocked once the run limit is reached (with a small grace band), so a run over the limit will fail with run_limit_reached.
Prompts
Create, version, and manage LLM prompt templates.
GET /api/v1/prompts
curl https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/prompts \ -H "Authorization: Bearer YOUR_TOKEN"
POST /api/v1/prompts
Required: name, template, llm_model Optional: description
curl -X POST https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/prompts \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "summarizer", "template": "Summarize: {{text}}", "llm_model": "gpt-4.1"}'
GET /api/v1/prompts/:id
PATCH /api/v1/prompts/:id
curl -X PATCH https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/prompts/1 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"template": "Summarize concisely: {{text}}"}'
DELETE /api/v1/prompts/:id
POST /api/v1/prompts/:id/publish
curl -X POST https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/prompts/1/publish \ -H "Authorization: Bearer YOUR_TOKEN"
Runs
Create runs, generate LLM responses, and judge them with metrics.
GET /api/v1/runs
Optional filters: status (pending, running, completed, failed), prompt_id, dataset_id, tag[]
curl "https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/runs?status=completed&prompt_id=1&tag[]=real%20estate" \ -H "Authorization: Bearer YOUR_TOKEN"
POST /api/v1/runs
Optional: name, prompt_id, dataset_id, metric_ids, judge_model, temperature, judge_temperature (defaults to 0 so re-judging is reproducible; above 0 the same output can score differently), max_tokens (cap on generated tokens per row; leave unset for the provider default, set it to match production when your prompt's output is long enough to truncate), output_column (score existing outputs: omit prompt_id and grade a dataset column instead, default actual_output), expected_column (dataset column holding each row's answer key, given to the judge and to checks that compare against the row's expected value, default expected_output)
curl -X POST https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/runs \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"prompt_id": 1, "dataset_id": 1, "metric_ids": [1, 2]}'
GET /api/v1/runs/:id
Also returns: metric_averages, a per-metric breakdown with each metric's average score (pass_rate for checks), the number of rows it graded, and low_count, how many scored below the medium quality threshold
curl https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/runs/1 \ -H "Authorization: Bearer YOUR_TOKEN"
POST /api/v1/runs/:id/generate
curl -X POST https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/runs/1/generate \ -H "Authorization: Bearer YOUR_TOKEN"
POST /api/v1/runs/:id/retry_failures
Optional: only (array of failed response IDs, to retry just a subset)
curl -X POST https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/runs/1/retry_failures \ -H "Authorization: Bearer YOUR_TOKEN"
POST /api/v1/runs/:id/rerun
curl -X POST https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/runs/1/rerun \ -H "Authorization: Bearer YOUR_TOKEN"
POST /api/v1/runs/:id/regrade
curl -X POST https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/runs/1/regrade \ -H "Authorization: Bearer YOUR_TOKEN"
GET /api/v1/runs/:id/compare?with=:other_id
curl "https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/runs/1/compare?with=2" \ -H "Authorization: Bearer YOUR_TOKEN"
PATCH /api/v1/runs/:id
curl -X PATCH https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/runs/1 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "baseline v2", "metric_ids": [1, 2, 3]}'
DELETE /api/v1/runs/:id
Responses
Read-only access to generated responses and their review scores. Nested under runs.
GET /api/v1/runs/:run_id/responses
Optional filters: status (pending, retrying, succeeded, failed), min_score, max_score, sort (id, score_asc, score_desc), plus limit and offset
Trim the payload: fields takes a comma-separated list of response keys and always returns id. Prefix with reviews. to trim each nested review, e.g. fields=score,reviews.metric_name,reviews.ai_score
curl "https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/runs/1/responses?sort=score_asc&limit=10&fields=id,score,reviews.metric_name,reviews.ai_score" \ -H "Authorization: Bearer YOUR_TOKEN"
GET /api/v1/runs/:run_id/responses/:id
Datasets
Data used as input for runs.
GET /api/v1/datasets
POST /api/v1/datasets
Required: name, and either csv_data (inline CSV) or a multipart file (CSV upload, preferred for large datasets)
curl -X POST https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/datasets \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "tickets", "csv_data": "text,expected_output\\nHello,Hi"}'
curl -X POST https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/datasets \ -H "Authorization: Bearer YOUR_TOKEN" \ -F "name=tickets" \ -F "file=@tickets.csv"
GET PATCH DELETE /api/v1/datasets/:id
curl -X PATCH https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/datasets/1 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "tickets-q3"}'
Metrics
Scoring dimensions used by the judge model.
GET /api/v1/metrics
POST /api/v1/metrics
Required: name Optional: metric_type (llm_judge default, or check), instruction and rubric_bands (array of {stars, description}) for judges, check_config (check_kind, target, plus per-kind keys) for checks
curl -X POST https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/metrics \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "relevance", "instruction": "Is the response relevant?"}'
GET PATCH DELETE /api/v1/metrics/:id
curl -X PATCH https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/metrics/1 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"instruction": "Is the response factually accurate?", "rubric_bands": [{"stars": 5, "description": "fully accurate"}]}'
Agreement loop
POST /api/v1/metrics/:id/suggest_variants
Optional: count, model
curl -X POST https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/metrics/1/suggest_variants \ -H "Authorization: Bearer YOUR_TOKEN"
Metric versions
GET /api/v1/metrics/:metric_id/metric_versions
GET /api/v1/metrics/:metric_id/metric_versions/:id
POST /api/v1/metrics/:metric_id/metric_versions/:id/publish
curl -X POST https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/metrics/1/metric_versions/5/publish \ -H "Authorization: Bearer YOUR_TOKEN"
DELETE /api/v1/metrics/:metric_id/metric_versions/:id
Metric Groups
Named groups of metrics you can apply to a run as a set.
GET /api/v1/metric_groups
POST /api/v1/metric_groups
Required: name Optional: description, metric_ids (array), tag_names (array)
curl -X POST https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/metric_groups \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "core quality", "metric_ids": [1, 2, 3]}'
GET PATCH DELETE /api/v1/metric_groups/:id
curl -X PATCH https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/metric_groups/1 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"metric_ids": [4, 5]}'
Agreements
Per-verdict feedback events on a response/metric pair: agree, disagree (with a corrected score and note), or borderline. Agreements capture the metric version that was current when the verdict was cast, which is what drives the trust signal and the "stale" indicators across the rest of the API.
GET /api/v1/agreements
Optional filters: run_id, response_id, metric_id, metric_version_id, created_by, verdict (agree, disagree, or borderline)
curl "https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/agreements?metric_id=1&verdict=disagree" \ -H "Authorization: Bearer YOUR_TOKEN"
POST /api/v1/runs/:run_id/responses/:response_id/metrics/:metric_id/agreements
Required: verdict (agree, disagree, or borderline); corrected_score (1–5) is required when verdict is disagree Optional: corrected_score (for agree/borderline), note, created_by (defaults to "api")
curl -X POST https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/runs/1/responses/42/metrics/3/agreements \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"verdict": "disagree", "corrected_score": 3, "note": "too generous", "created_by": "alice"}'
GET /api/v1/runs/:run_id/responses/:response_id/metrics/:metric_id/agreements
DELETE /api/v1/agreements/:id
Tags
Domain labels you can attach to metrics, prompts, runs, datasets, and metric groups. Tags are auto-assigned a color from a 10-color palette. Each of those five resources' list endpoints can be filtered by one or more tags using ?tag[]=name query params (OR semantics: a record matching any of the given tags is returned).
GET /api/v1/tags
curl https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/tags \ -H "Authorization: Bearer YOUR_TOKEN"
POST /api/v1/tags
Required: name
curl -X POST https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/tags \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "real estate"}'
GET PATCH DELETE /api/v1/tags/:id
Tagging resources
curl -X POST https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/metrics \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Accuracy", "tag_names": ["real estate"]}'
MCP tools
tags_listList all tagstags_getGet a tag by IDtags_createCreate a tag (name required)tags_updateUpdate a tag's nametags_deleteDelete a tag and remove all its taggingsProvider Credentials
LLM provider API keys. The api_key field is write-only and never returned in responses.
GET /api/v1/provider_credentials
POST /api/v1/provider_credentials
Required: provider (openai, anthropic, ollama, openrouter, azure_foundry) Optional: api_key (not enforced by the API, but required in practice for hosted providers; local providers like ollama can omit it), api_endpoint (required when provider is azure_foundry), api_version
curl -X POST https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/provider_credentials \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"provider": "openai", "api_key": "sk-..."}'
curl -X POST https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/provider_credentials \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"provider": "azure_foundry", "api_key": "...", "api_endpoint": "https://my-resource.openai.azure.com", "api_version": "2024-10-21"}'
GET PATCH DELETE /api/v1/provider_credentials/:id
curl -X PATCH https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/provider_credentials/1 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"api_key": "sk-new..."}'
Imports
Bring an existing promptfoo config into CompletionKit in one call. Prompts, the test dataset, and assert-based metrics are created where they map cleanly and skipped with a reason where they don't. Providers are matched against your already-configured credentials; any referenced in the config but not yet configured are reported so you can add them.
POST /api/v1/imports/promptfoo
Request: config (the YAML text) or a raw YAML request body
Response 201: prompts and metrics each list what was created and what was skipped (with a reason); dataset is {created, rows, columns} (or {skipped}); providers is {matched, unmatched} (provider strings from the config)
curl -X POST https://completionkit.com/orgs/your-org-slug-goes-here/api/v1/imports/promptfoo \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/x-yaml" \ --data-binary @promptfooconfig.yaml
Plan quotas
Each plan includes a monthly allowance of runs and prompt fetches. Usage counts per billing period and resets when the period rolls over. The same allowance applies whether you go through the app, the REST API, or the MCP server. Deleting a run does not return quota: the period counter only moves up, so a deleted-then-recreated run still counts twice.
Pre-check your usage
Both surfaces expose the same read so you can see what's left before starting work: GET /api/v1/usage over REST, or the usage_get MCP tool. Either returns both counters for the current period. Unlimited plans report limit and remaining as null.
GET /api/v1/usage
{
"runs": { "used": 42, "limit": 100, "remaining": 58, "percent_used": 42, "over_limit": false, "blocked": false },
"prompt_fetches": { "used": 10, "limit": 1000, "remaining": 990, "percent_used": 1, "over_limit": false, "blocked": false },
"period": { "start": "2026-07-01T00:00:00Z", "end": "2026-07-31T23:59:59Z", "resets_at": "2026-08-01T00:00:00Z" }
}
Over the limit
Runs are hard-blocked once the run limit is reached (a small grace band is allowed first), surfaced with structured fields on both surfaces. Prompt fetches are never blocked for being over quota: your app depends on them at runtime, so once over the limit they keep serving and only warn (owner email plus a heads-up to your team). We count them so you can see the overage, but we won't brick a live app.
REST 402 Payment Required
{
"error": "quota_exceeded",
"scope": "runs",
"message": "Run limit reached for this billing period.",
"used": 100, "limit": 100,
"resets_at": "2026-08-01T00:00:00Z",
"upgrade_url": "https://completionkit.com/pricing"
}
MCP runs_create error
{
"error": "quota_exceeded",
"code": "run_limit_reached",
"scope": "runs",
"retryable": false,
"used": 100, "limit": 100,
"resets_at": "2026-08-01T00:00:00Z",
"upgrade_url": "https://completionkit.com/pricing"
}
Approaching the limit
Once usage crosses 80% of the run limit, a successful runs_create still succeeds but carries a non-fatal usage_warning in its structuredContent, so an agent can slow down ahead of the block instead of discovering it by hitting it.
{
"usage_warning": {
"code": "run_limit_approaching",
"used": 85, "limit": 100, "remaining": 15,
"resets_at": "2026-08-01T00:00:00Z"
}
}
