ES EN
Avanzado

API REST pública — v1

Acceso programático a los mismos primitivos de QA que expone la UI de ArtificialQA. Disparar test executions desde CI/CD, integrar evaluación en una plataforma de agentes interna, o construir dashboards sobre tus runs.

Estado: v1 — estable. Los breaking changes salen como /api/v2/public/. Los cambios aditivos (nuevos endpoints, nuevos campos, nuevos params opcionales) salen sin bump de versión. Revisión actual: v1.6.4.

Base URLs

OpenAPI spec: /openapi.yaml — pasalo a cualquier herramienta OpenAPI (Postman, Stoplight, Speakeasy, openapi-generator, etc.) para generar un cliente tipado.

¿Quieres una superficie chat-friendly? Los mismos primitivos están expuestos sobre Model Context Protocol en /api/v1/mcp. Usá esa ruta para integrar con Claude Desktop, Cursor, Continue o cualquier agente compatible con MCP.

1. Autenticación

Cada request necesita una API key en el header Authorization:

Authorization: Bearer aqa_live_xxxxxxxxxxxxxxxxxxxxxxxxx

Generar una key

  1. Inicia sesión en ArtificialQA → clic en tu avatar → API Keys & MCP → pestaña API KeysNew API Key.
  2. Elige el proyecto al que la key queda atada. Solo va a poder leer/escribir datos dentro de ese proyecto.
  3. Elige un scope:
    • read — solo GET. Para dashboards, reporting, integraciones read-only.
    • write — GET + POST. Necesario para disparar ejecuciones / evaluaciones.
  4. Elige una fecha de expiración (v1.6.3 — obligatoria). Mínimo 1 día, máximo 12 meses desde hoy. Default 6 meses. No hay opción de "no expira nunca" — anotá un recordatorio para rotar.
  5. Copia la key en texto plano — se muestra una sola vez. Guárdala en un secret manager.

Quién puede crear keys:

Las keys tienen scope por proyecto (v1.6.2). Nunca exponen datos de otro proyecto, ni entre organizaciones ni entre proyectos de la misma organización. Si necesitas acceso a múltiples proyectos, creá una key por proyecto. ¿Perdiste una key? Revócala desde la misma UI y genera una nueva.

Semántica de audit — las keys actúan como servicio (v1.6.3)

Cuando una API key crea o modifica datos vía esta API REST, el audit log registra el actor como service:<prefix de la key>, no el email del humano que la generó. El humano que la creó queda capturado aparte en details.createdByUserId para trazabilidad. De un vistazo el auditor ve "esto lo hizo una máquina, esta es la key" en lugar de atribuir engañosamente miles de operaciones de CI a una sola persona.

Las entradas legacy (pre-v1.6.3) conservan su string original apikey:<prefix> — el discriminador en la tabla api_keys (actorKind) le dice al audit writer qué formato emitir.

Binding por proyecto (v1.6.2)

Cada API key está atada a exactamente un proyecto. Comportamiento:

OAuth (solo MCP)

Hay un segundo camino de autenticación — tokens Bearer oat_* — disponible exclusivamente en el endpoint MCP (/api/v1/mcp). Es el camino que usan Claude Desktop, Cursor y otros clientes MCP manejados por personas: en lugar de pegar una key estática, el usuario hace login en ArtificialQA en el navegador y elige un proyecto en una pantalla de consentimiento. La API REST (/api/v1/public) sigue usando solo keys estáticas aqa_* — los tokens OAuth no se aceptan en las rutas REST. Configuración: Conectar por MCP.

Errores

StatusCodeCuándo
401missing_tokenFalta el header Authorization.
401invalid_tokenKey no encontrada o malformada.
401token_revokedKey revocada desde la UI.
401token_expiredKey pasó su expiresAt.
403insufficient_scopeKey read intentó un POST.

2. Convenciones

Asíncrono + polling

Todos los endpoints que cambian estado son asíncronos. El POST devuelve 202 Accepted inmediatamente con un executionId / evaluationId y un statusUrl. Los clientes hacen polling al GET hasta que el status llega a estado terminal.

EndpointEstados terminales
/executions/{id}completed, failed, cancelled
/evaluations/{id}completed, failed

No hay webhooks en v1.

Cadencia de polling

Un cliente razonable hace polling con backoff exponencial, capeado en 10 segundos:

delay = min(10, 1.5 ** intento) segundos

La mayoría de las ejecuciones terminan en menos de 5 minutos; las ejecuciones browser con Playwright pueden correr 15+ minutos.

Errores — RFC 7807 problem+json

Cada respuesta de error tiene content-type: application/problem+json y este body:

{
  "type":   "https://docs.artificialqa.com/errors/<code>",
  "title":  "Resumen corto legible por humanos",
  "status": 404,
  "code":   "not_found",
  "detail": "Explicación verbose opcional."
}

Haz switch sobre code, no sobre title o detail. Los titles pueden reescribirse entre releases; los codes son parte del contrato.

Catálogo completo:

StatusCodeSignificado
400missing_path_paramURL malformada — típicamente un UUID faltante.
400validation_failedBody, query o header no pasó validación.
401missing_token / invalid_token / token_revoked / token_expiredAuth (ver sección 1).
402quota_exceededCuota mensual alcanzada (ejecuciones, evaluaciones o tokens).
403insufficient_scopeEl scope de la key es read pero la ruta requiere write.
403mcp_disabledLa organización tiene la feature MCP apagada (solo ruta MCP).
404not_foundEl recurso no existe en esta org / proyecto.
409invalid_stateEl recurso está en un estado que bloquea la operación.
409empty_planEl plan tiene cero test cases activos.
409evaluation_in_progressHay una evaluación previa sobre esta ejecución todavía pending o running.
409no_evaluatorsLa org no tiene evaluadores activos configurados.
409duplicate_membershipLa fila de membership ya existe (suite item o plan-suite link).
429rate_limit_exceededRate limit por key alcanzado.
500internal_errorError inesperado del servidor — abrí un ticket.

Idempotencia

Los endpoints POST aceptan un header Idempotency-Key (1-255 chars). Reutilizar la misma key dentro del proyecto devuelve la fila existente con idempotent: true y HTTP 200 en lugar de crear un duplicado y devolver 202.

POST /api/v1/public/test-plans/<id>/executions
Authorization: Bearer aqa_live_...
Content-Type: application/json
Idempotency-Key: ci-build-4815162342

{ "agentConnectionId": "..." }

Las idempotency keys tienen scope por proyecto (v1.6.2), persisten indefinidamente. Un patrón típico es usar el CI build ID, el commit SHA, o un UUIDv4 generado por intento. Para POST /test-cases/import el TTL del cache es 24h.

Legacy: el request body también acepta un campo deprecado idempotencyKey. Si mandas ambos, gana el header. Migrá al header.

Rate limits

Cada key tiene rate limit independiente. En cada respuesta:

X-RateLimit-Limit:     <max-por-ventana>
X-RateLimit-Remaining: <restante-en-ventana>
X-RateLimit-Reset:     <unix-seconds-cuando-resetea>

Un 429 agrega Retry-After: <segundos>. El body es un problem+json estándar con code: "rate_limit_exceeded". El pool de rate-limit es compartido entre keys estáticas aqa_* y tokens OAuth oat_* — ambos llaveados por prefix.

Cuotas

Las ejecuciones, evaluaciones y tokens LLM cuentan contra la cuota mensual del plan de la org. Pegarle a cualquiera devuelve 402 quota_exceeded. El body indica qué cuota:

{
  "code": "quota_exceeded",
  "detail": "Monthly execution limit reached (250/250)."
}

3. El flujo

El flujo estándar de integración con CI es 4 llamadas:

 1. GET  /test-plans                       descubrir id del plan
 2. GET  /agent-connections                descubrir id de la conexión
 3. POST /test-plans/{id}/executions       disparar ejecución
                                           (opcional evaluate=true)
 4. GET  /executions/{id}    (polling)     esperar estado terminal

Si evaluate: true se seteó en el paso 3, la evaluación corre automáticamente después usando todos los evaluadores permitidos por el tier de la org — GET /executions/{id}.evaluation trae el resumen. Si no:

 5. (opcional) GET /evaluators              descubrir ids de evaluadores
 6. POST /executions/{id}/evaluations       disparar evaluación explícitamente
                                            (opcional con evaluatorIds)
 7. GET  /evaluations/{id}   (polling)      esperar estado terminal

El happy path más corto (evaluate: true) es 2 llamadas + polling.

4. Endpoints

Test plans

GET /test-plans

Lista cada plan en el proyecto de la API key.

Query params: status, limit.

Response 200 — array de TestPlanSummary:

[
  {
    "id": "744cd22e-bf76-4e8c-8060-9f283a64796c",
    "name": "TP_Browser_ArtQA",
    "description": null,
    "status": "completed",
    "environment": null,
    "defaultAgentConnectionId": "12b17e7b-...",
    "suiteCount": 3,
    "executionCount": 7,
    "createdAt": "2026-05-21T13:44:09.103Z",
    "updatedAt": "2026-06-10T17:01:55.211Z"
  }
]

GET /test-plans/{id}

Detalle con desglose de suites. Útil para elegir un plan en una UI.

POST /test-plans — crear (v1.4)

Crea un plan vacío. Requerido: name. Opcional: description, status (default "draft"), agentConnectionId, environment, scheduleCron, projectId (auto si la org tiene exactamente un proyecto activo; requerido si no — el error enumera candidatos). El runner es el único que puede escribir running y completed; create acepta solo {draft, ready}.

Response 201TestPlanDetail completo con createdBy="apikey:<prefix>", suites=[], executionCount=0.

PATCH /test-plans/{id} — actualizar (v1.4)

Update parcial. Patcheables: name, description, status (subset {draft, ready, archived}), agentConnectionId, environment, scheduleCron. No patcheables: projectId (huérfanaría las memberships de suite), createdBy, createdAt, isActive (usar DELETE).

PATCH sobre un plan en running se rechaza con 409 invalid_state — el runner está a mitad del snapshot.

DELETE /test-plans/{id} — soft-delete (v1.4)

Pone isActive=false. Idempotente. Las ejecuciones pasadas que snapshottearon el plan siguen funcionando. Rechazado con 409 invalid_state si el plan está actualmente running.

POST /test-plans/{id}/suites — enlazar una suite (v1.4)

{ "testSuiteId": "uuid", "sortOrder": 5 }

sortOrder opcional — si lo omites el servidor asigna MAX(sortOrder)+1. El link de suites tiene scope por proyecto: link cross-project → 409 invalid_state con ambos project ids en detail. Link duplicado → 409 duplicate_membership.

DELETE /test-plans/{id}/suites/{suiteId} — desvincular (v1.4)

Quita el enlace. Idempotente. Actualiza plan.updatedAt. Rechazado con 409 invalid_state si el plan está actualmente running.

Forma de TestPlanSummary:

{
  id: uuid,
  name: string,
  description: string | null,
  status: "draft" | "ready" | "running" | "completed" | "archived",
  environment: string | null,
  defaultAgentConnectionId: uuid | null,
  suiteCount: int,
  executionCount: int,
  createdAt: ISO,
  updatedAt: ISO,
}

Test suites

GET /test-suites

Lista paginada con cursor: { data: TestSuiteSummary[], nextCursor: string | null }. Query params: cursor, limit (1-200, default 50), projectId, containsTestCaseId, tags, search, createdAfter, updatedSince, isActive.

GET /test-suites/{id}

Detalle completo. items[] ordenado por (sortOrder ASC, id ASC), cada uno con un summary embebido del testCase. 404 sobre suites soft-deleted salvo que pases ?isActive=false.

POST /test-suites

Crea una suite vacía. Requerido: name. Opcional: description, tags, projectId (auto si la org tiene un proyecto activo; requerido si no). La membership va por los endpoints sub-resource de abajo — por diseño, defines la estructura por separado del contenido.

PATCH /test-suites/{id}

Update parcial. Patcheables: name, description (manda null para limpiar), tags, isActive. No patcheables: projectId, testCount (mutado solo vía endpoints de membership), createdBy, createdAt.

DELETE /test-suites/{id}

Soft-delete. Idempotente. Las join rows sobreviven, así que cualquier plan que referenciara la suite sigue resolviendo.

POST /test-suites/{id}/test-cases

Agrega una membership.

{ "testCaseId": "uuid", "sortOrder": 5 }

Scope por proyecto: un test case solo puede agregarse a una suite del mismo proyecto. Add cross-project → 409 invalid_state. Duplicado → 409 duplicate_membership.

DELETE /test-suites/{id}/test-cases/{tcId}

Quita una membership. Idempotente. Quitar un TC que no era miembro devuelve 204 sin escrituras en DB.

Test cases

CRUD completo más bulk import. Permite que un CI / IaC / script de agente autore y curate test cases sin tocar la UI.

Decisiones de contrato:

GET /test-cases

Lista paginada con cursor: { data: TestCaseSummary[], nextCursor: string | null }.

Query params: cursor, limit (1-200, default 50), suiteId, type, difficulty, lifecycleStatus, reviewStatus, source, industryId, piiDetected, tags (repetido, ANY-match), search, createdAfter, updatedSince, isActive.

GET /test-cases/{id}

Detalle completo — cada columna de TestCase menos campos encriptados / internos. Devuelve tanto la forma legacy como la design.

POST /test-cases

Requerido: type más O design, O input/expectedOutput, O turns (para conversational). El boundary Zod atrapa el caso conversational-sin-turns antes de cualquier escritura en DB.

Resolución de proyecto: si la org tiene exactamente un proyecto activo, projectId se elige automáticamente. Si no, es requerido y el detail del error enumera los proyectos disponibles con (id, name).

Response 201TestCaseDetail completo. source=manual, reviewStatus=approved.

PATCH /test-cases/{id}

Update parcial. El body tiene que tener al menos un campo. No se puede cambiar type, source, ni reviewStatus. El PII se re-escanea solo cuando el patch toca input / expectedOutput / turns / design / tags.

DELETE /test-cases/{id}

Soft-delete. Idempotente — re-DELETE sobre una fila archivada devuelve el mismo 204 sin escritura.

POST /test-cases/import — bulk

Hasta 500 items por request. Independencia por fila: una fila mala va a errors[] y el batch sigue.

Idempotencia: con Idempotency-Key el call es replay-safe — cacheado contra (project, key, "test_case") en la tabla bulk_imports por 24 horas. Una invocación repetida dentro de esa ventana devuelve el resultado original verbatim con idempotent: true y cero mutaciones en DB.

{
  "createdIds": ["uuid-1", "uuid-2", ...],
  "errors": [{ "index": 2, "code": "not_found", "detail": "industryId ... does not exist" }],
  "idempotent": false
}

Status codes: 200 éxito total (o replay del cache), 207 Multi-Status éxito parcial (el mismo status aplica en replay), 400 rechazo de schema, 402 cuota.

Agent connections

GET /agent-connections

Lista conexiones. Nunca devuelve secrets — solo los campos necesarios para elegir una conexión.

Query params: protocol (http | browser | websocket), isActive.

[
  {
    "id": "12b17e7b-2cec-4206-986d-9df390be2de3",
    "name": "ArtificialQA Test Bank",
    "protocol": "browser",
    "baseUrl": "https://app.artificialqa.com",
    "isActive": true,
    "createdAt": "2026-05-19T09:12:11.502Z"
  }
]

GET /agent-connections/{id} — detalle (v1.5)

Devuelve el AgentConnectionDetail completo con secrets enmascarados como "***<last4>". Útil para inspeccionar la config existente antes de un PATCH.

POST /agent-connections — crear (v1.5)

Requerido: name, protocol, baseUrl, authConfig, messageConfig. Opcional: description, environment (default "production"), preChatConfig, postChatConfig, templateVars, environments, projectId.

El caller manda secrets en texto plano en los blobs de config; el helper los encripta (AES-256-GCM) antes del INSERT y los guarda como enc:<iv>:<tag>:<ciphertext>.

PATCH /agent-connections/{id} — actualizar (v1.5)

Update parcial. Patcheables: name, description, baseUrl, environment, isActive, authConfig, preChatConfig, messageConfig, postChatConfig, templateVars, environments. No patcheables: projectId, protocol.

Round-trip de secrets enmascarados: mandar un valor enmascarado (ej. "***c123") en una ruta de secret-named cae al valor desencriptado de la DB existente — puedes leer el detalle, editar cualquier campo en texto plano, y volver a hacer PATCH de toda la config sin manejar manualmente secrets. Solo los valores NUEVOS en texto plano sobreescriben el secret guardado.

Los blobs de config se reemplazan por key, no se hace deep-merge. Si omites una key en tu PATCH, se pierde de la columna. null explícito limpia un blob nullable.

DELETE /agent-connections/{id} (v1.5)

Soft-delete. Idempotente. Las ejecuciones / plans pasados siguen funcionando. Para reactivar: PATCH { "isActive": true }.

POST /agent-connections/{id}/test — smoke-test (v1.5)

Smoke-test sincrónico contra el agente configurado. Body opcional: { "runtimeVars": { ... } } mergeado sobre los templateVars persistidos solo para este call.

Comportamiento por protocolo:

Rate limit de 1 call por 60s por (connectionId, apiKeyId).

{
  "status": "completed",
  "ok": true,
  "latencyMs": 1234,
  "error": null,
  "details": { "protocol": "http", "statusCode": 200, "response": "..." },
  "startedAt": "2026-06-17T10:00:00.000Z",
  "completedAt": "2026-06-17T10:00:01.234Z"
}
Forward-compat: status es un discriminador. v1.5 siempre setea "completed" porque el endpoint es sincrónico. Haz pattern-match sobre status como union para que un futuro "pending" + polling async no rompa los clientes.

Ejecuciones

POST /test-plans/{id}/executions — disparar

Arranca un run en background. Devuelve 202 con el nuevo id; 200 si es idempotente.

{
  "agentConnectionId": "12b17e7b-...",
  "evaluate": true,
  "evaluatorIds": ["ad12-...", "be34-..."],
  "evaluatorWeights": { "ad12-...": 2.0 },
  "runtimeVars": { "documentId": "doc-42" }
}
CampoTipoRequeridoNotas
agentConnectionIduuid
evaluatebool, default falsenoDispara automáticamente la evaluación cuando termina la ejecución.
evaluatorIdsuuid[]noSolo se respeta cuando evaluate: true. Restringe la auto-eval. Omitir → corren todos los evaluadores permitidos por el tier.
evaluatorWeights{uuid: number > 0}noSolo se respeta cuando evaluate: true. Override de peso por evaluador. Las omitidas usan el peso default configurado del evaluador.
runtimeVars{string: string}noSubstituciones de template vars para la agent connection.
idempotencyKeystring, deprecadonoPreferí el header Idempotency-Key.

Response 202:

{
  "executionId": "9e2f-...",
  "status": "pending",
  "statusUrl": "https://app.artificialqa.com/api/v1/public/executions/9e2f-..."
}

Response 200 (replay idempotente): misma forma más "idempotent": true.

409 comunes: invalid_state (plan no en ready/completed), empty_plan.

GET /executions — list

Filtros: testPlanId, status, limit. Devuelve ExecutionSummary[] — misma forma que el detalle pero sin results[].

GET /executions/{id} — detalle

Devuelve la ejecución + cada resultado + la evaluación más reciente (si hay).

{
  "id": "9e2f-...",
  "testPlanId": "744cd22e-...",
  "agentConnectionId": "12b17e7b-...",
  "runNumber": 8,
  "status": "completed",
  "totalCases": 12,
  "completedCases": 12,
  "failedCases": 1,
  "durationMs": 184320,
  "errorMessage": null,
  "createdAt": "2026-06-10T17:02:11.040Z",
  "completedAt": "2026-06-10T17:05:15.360Z",
  "results": [
    {
      "id": "...",
      "testCaseId": "...",
      "executionStatus": "SUCCESS",
      "responseValidity": "VALID",
      "responseTimeMs": 8234,
      "success": true,
      "retryCount": 0,
      "finalized": true,
      "createdAt": "..."
    }
  ],
  "evaluation": {
    "id": "ad12-...",
    "status": "completed",
    "runNumber": 1,
    "overallScore": 0.87,
    "passRate": 0.92,
    "passed": true,
    "totalCases": 12,
    "passedCases": 11,
    "failedCases": 1,
    "durationMs": 32104
  }
}

executionStatus: SUCCESS, ERROR, TIMEOUT, SKIPPED. responseValidity: VALID, EMPTY, MALFORMED. Solo los resultados SUCCESS + VALID entran a evaluación.

Evaluadores

GET /evaluators

Lista los evaluadores visibles para la org — los globales de la plataforma más cualquier evaluador custom de la org. Úsalo para descubrir el id que pasas como evaluatorIds.

Query params: includeBlocked (default true).

Seguridad. Nunca expone el agentConfig del evaluador (credenciales encriptadas) ni el systemPrompt (prompt de calibración).

CampoNotas
isGlobaltrue para los globales de plataforma; false para custom de la org (Enterprise).
planAllowedtrue si tu tier de billing permite este evaluador. false significa que un POST /evaluations que lo referencie lo va a descartar silenciosamente del run. Filtra sobre este campo del lado del cliente.
weightPeso default aplicado al calcular el overall score. Override por run vía evaluatorWeights en el POST.

Evaluaciones

POST /executions/{id}/evaluations — disparar

Corre los evaluadores configurados sobre una ejecución completed. Body (todo opcional):

{
  "evaluatorIds": ["uuid", "uuid"],
  "evaluatorWeights": { "uuid": 2.0 }
}

Si se omite evaluatorIds, corren todos los evaluadores activos permitidos por el tier. El default de pesos coincide con la UI: cualquier evaluador no presente en evaluatorWeights usa su peso default configurado de GET /evaluators.

409 comunes: invalid_state (ejecución todavía no completed), evaluation_in_progress, no_evaluators.

GET /evaluations/{id} — detalle

Devuelve el run + cada score por test case. Mientras status: "running" la respuesta incluye scoresCompleted / scoresTotal para una progress bar.

{
  "id": "ad12-...",
  "executionId": "9e2f-...",
  "runNumber": 1,
  "status": "completed",
  "overallScore": 0.87,
  "passRate": 0.92,
  "passed": true,
  "totalCases": 12,
  "passedCases": 11,
  "failedCases": 1,
  "durationMs": 32104,
  "createdAt": "...",
  "completedAt": "...",
  "scores": [
    {
      "id": "...",
      "evaluatorId": "...",
      "evaluatorName": "Tone",
      "evaluatorSlug": "tone",
      "weight": 1.0,
      "testCaseId": "...",
      "score": 0.83,
      "passed": true,
      "explanation": "Response stayed polite and professional throughout.",
      "createdAt": "..."
    }
  ]
}
scores[].weight es el peso de runtime que efectivamente se aplicó al calcular overallScore. Si pasaste evaluatorWeights en el trigger, coincide con ese override; si no, coincide con el default configurado del evaluador. En evaluaciones legacy puede ser null.

Reportes de evaluación (v1.6)

4 endpoints + 2 endpoints PDF que exponen el pipeline de reportes de evaluación: resumen ejecutivo, análisis por evaluador, scores por test case y un PDF. GET /report es read-only — nunca llama al LLM, nunca consume tokens, los resúmenes faltantes salen como strings vacíos ("").

Decisiones de contrato:

GET /evaluations/{id}/report

Devuelve el reporte JSON consolidado. Read-only.

GET /evaluations/{id}/report-pdf

Devuelve el PDF como application/pdf con Content-Disposition: attachment; filename="report_<plan>_eval<runNumber>_<YYYY-MM-DD>.pdf". Read-only.

curl -H "Authorization: Bearer aqa_xxx" \
  https://app.artificialqa.com/api/v1/public/evaluations/<id>/report-pdf \
  -o report.pdf

POST /evaluations/{id}/report-pdf-url — URL tokenizada (v1.6.1)

Emite una URL tokenizada de corta duración (TTL 1h) que descarga el PDF sin necesidad de header Authorization. Útil para pasarle un link de clic-y-listo a una persona o a un cliente LLM que no puede hacer round-trip con el Bearer token.

{
  "url": "https://app.../api/v1/public/downloads/<token-de-43-chars>",
  "expiresAt": "2026-06-17T18:30:00.000Z",
  "expiresInSec": 3600
}

La URL es multi-uso dentro del TTL, con scope por organización, propósito único (desbloquea UN solo PDF de evaluación), y Cache-Control: no-store. NO gasta tokens y NO requiere ningún feature gate.

GET /downloads/{token} — público, sin Authorization

Transmite el binario PDF. No se requiere API key — el token es la auth. Token faltante / malformado / expirado → 404 unificado (NO 410) para que la superficie no filtre la existencia de tokens pasados o ajenos.

POST /evaluations/{id}/summary

Regenera el resumen ejecutivo. Body (opcional): { "lang": "en", "force": false }.

{
  "summary": "Generated text...",
  "providerName": "saia",
  "model": "agent-v1",
  "cached": false,
  "tokensUsed": 1234
}

POST /evaluations/{id}/evaluator-summary

Regenera el análisis de un evaluador dentro de esta evaluación. Body: { "evaluatorId": "uuid", "lang": "en", "force": false }. evaluatorId requerido. Misma forma SummaryResult que la variante ejecutiva.

5. Ejemplos de código

Hay ejemplos ejecutables disponibles en tres sabores, cada uno implementa el flujo completo — descubrir → disparar → polling → imprimir scores:

Para un cliente tipado en cualquier lenguaje, generalo desde la spec OpenAPI.

6. Límites y edge cases

El filtrado de evaluadores por tier es async-silent. Cuando pasas evaluatorIds, el pre-check sincrónico verifica que los ids pertenezcan a tu org pero no aplica el whitelist del tier de billing — eso corre async en el runner. Efecto neto: los ids con planAllowed: false se descartan silenciosamente y el scores[] simplemente los omite. Si todos los ids pedidos se descartan, la evaluación termina con status: "failed" y errorMessage: "No active evaluators configured". Filtra del lado del cliente sobre planAllowed: true de GET /evaluators antes de mandar. Fix en el roadmap v1.x.
¿Necesitas acceso? La API REST pública está disponible en los planes Pro y Enterprise. Genera una key desde tu avatar → API Keys & MCPAPI Keys, o escríbenos desde artificialqa.com.