Council Validation Events API
Send your multi-model council runs to Consilens so the value of competitive collaboration (one model catching what another missed) is measured. Your data is tenant-isolated: your credentials can only ever write events tagged to your own account, and your raw text is never shared.
Credentials
You receive one workspace API token (prefix cws_). Treat it as a secret — store it in your environment, never in client code or version control. It is scoped to your account/project; a request naming any other tenant is rejected with 403.
Endpoint
POST https://consilens.dev/api/workspace/validation-events
Authorization: Bearer <your cws_ token>
Content-Type: application/json
Request body
| field | type | required | notes |
|---|---|---|---|
| accountId | string | yes | your account id (e.g. account:refractal) |
| workspaceId | string | no | your workspace id |
| projectId | string | yes | your project id (e.g. project:refractal) |
| orchestrationRunId | string | yes | stable id per convene — drives idempotency |
| triggerMessageId | string | yes | id of the message that triggered the council |
| selectedModels | string[] | yes | model ids that participated |
| synthesisModel | string | no | model that synthesized the answer |
| councilMembers | object[] | yes | per-member take (displayName, provider, model, take, dissent[], recommendedPath[]) |
| costEstimate | object | no | optional cost metadata |
Example:
{
"accountId": "account:refractal",
"projectId": "project:refractal",
"orchestrationRunId": "run-2026-06-08-0042",
"triggerMessageId": "msg-8831",
"selectedModels": ["gpt-5.5", "claude-opus-4-7", "gemini-3-pro"],
"councilMembers": [
{ "displayName": "Claude", "provider": "anthropic", "model": "claude-opus-4-7",
"take": "Ship it.", "dissent": ["index lock risk under load"], "recommendedPath": ["CREATE INDEX CONCURRENTLY"] }
]
}
Response
201 { "written": 2, "ids": ["cve:run-...:run", "cve:run-...:dissent:0"] }
| status | meaning |
|---|---|
| 201 | events recorded |
| 400 | malformed payload |
| 401 | missing/invalid/expired token |
| 403 | the payload's tenant does not match your token's tenant |
| 429 | rate limited — back off and retry |
Idempotency
Event ids derive deterministically from your orchestrationRunId, so re-POSTing the same run never double-counts. On a 5xx or network error, just retry the same body.
Minimal client (TypeScript)
export async function reportCouncilRun(run, token) {
const res = await fetch("https://consilens.dev/api/workspace/validation-events", {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: "Bearer " + token },
body: JSON.stringify(run)
});
if (!res.ok) throw new Error("validation-events " + res.status);
return res.json();
}
Call it after each convene (fire-and-forget with a retry is fine).