API de eventos de auditoría
Probara expone dos endpoints de solo lectura para listar auditoría:
| Endpoint | Alcance | Rol mínimo |
|---|---|---|
GET /api/v1/orgs/{orgUlid}/audit-events | Toda la organización | Owner o admin |
GET /api/v1/runs/{runUlid}/audit-events | Una ejecución concreta | Cualquier miembro que pueda leer el run (viewer+) |
Ambos devuelven el mismo sobre AuditEventOut con paginación por cursor. Los eventos se ordenan por ULID descendente (más recientes primero).
Listado de toda la organización
Sección titulada «Listado de toda la organización»GET /api/v1/orgs/{orgUlid}/audit-events devuelve el registro append-only de auditoría de una organización.
Autorización
Sección titulada «Autorización»- Sesión: inicia sesión en el navegador y envía la cookie de sesión más
X-Organization-Idsi llamas desde scripts propios. - Bearer: token de API con alcance de organización y membresía owner o admin en esa org.
Los miembros y visualizadores reciben 403 Forbidden.
Parámetros de consulta
Sección titulada «Parámetros de consulta»| Parámetro | Tipo | Descripción |
|---|---|---|
limit | entero | Tamaño de página. Por defecto 50. Valores mayores a 100 se limitan silenciosamente a 100. |
cursor | ULID | Si está presente, devuelve eventos con ulid < cursor (página más antigua). |
Forma de la respuesta
Sección titulada «Forma de la respuesta»{ "events": [ { "ulid": "01J…", "action": "project.created", "actor": { "kind": "user", "ulid": "01J…", "email": "alice@example.com", "displayName": null }, "entityType": "project", "entityUlid": "01J…", "entityLabel": "Acme Web", "runUlid": null, "metadata": {}, "createdAt": 1700000000000 } ], "nextCursor": "01J…"}nextCursor es el ULID del último evento en events cuando hay otra página; si no, null.
runUlid aparece en eventos ligados a un run (y en el listado org-wide cuando el run padre sigue existiendo). Los eventos que no son de run omiten el campo o lo envían como null.
Listado por run
Sección titulada «Listado por run»GET /api/v1/runs/{runUlid}/audit-events devuelve solo los eventos cuyo run_id corresponde al run resuelto dentro de la organización activa. Usa la misma cookie de sesión o token bearer que el resto de APIs de runs; en sesión de navegador envía X-Organization-Id.
La autorización coincide con GET /api/v1/runs/{runUlid}: cualquier miembro de la organización con acceso al run puede listar su historial. No hay restricción owner/admin en esta ruta.
Los parámetros de consulta y el sobre de respuesta son idénticos al endpoint org-wide (limit, cursor, events, nextCursor). Un 404 indica que el ULID del run no existe en la organización activa.
Ejemplo:
export PROBARA_API_TOKEN="probara_…"export RUN_ULID="01J…"
curl -sS \ -H "Authorization: Bearer $PROBARA_API_TOKEN" \ "https://probara.net/api/v1/runs/${RUN_ULID}/audit-events?limit=50"Variantes de actor
Sección titulada «Variantes de actor»{ "kind": "api_token", "ulid": "01J…", "name": "CI deploy bot" }{ "kind": "system" }Si un usuario o token fue eliminado, los campos de referencia son null pero el evento sigue en la lista:
{ "kind": "user", "ulid": null, "email": null, "displayName": null }Ejemplo
Sección titulada «Ejemplo»export PROBARA_API_TOKEN="probara_…"export ORG_ULID="01J…"
curl -sS \ -H "Authorization: Bearer $PROBARA_API_TOKEN" \ "https://probara.net/api/v1/orgs/${ORG_ULID}/audit-events?limit=50"Página siguiente:
curl -sS \ -H "Authorization: Bearer $PROBARA_API_TOKEN" \ "https://probara.net/api/v1/orgs/${ORG_ULID}/audit-events?cursor=${NEXT_CURSOR}"Metadatos y diffs estructurados
Sección titulada «Metadatos y diffs estructurados»metadata es un objeto que puede incluir buckets de diff compartidos (documentados en OpenAPI como AuditEventDiff, AuditAssigneeChange y AuditRunAggregates) más claves de snapshot propias de cada acción. Los integradores DEBEN tratar claves desconocidas como compatibles hacia adelante.
Buckets de diff habituales:
| Clave | Usada por | Forma |
|---|---|---|
changes | *.updated, run.case_marked, run.case_step_marked, run.case_retried, run.case_step_actual_result_set, run.result_attachment_committed (cuando también cambió el texto) | AuditFieldChange[] — from / to escalares con fromLabel / toLabel opcionales en referencias |
assigneeChange | run.case_assigned | { from: { ulid, name } | null, to: { ulid, name } | null, via: 'open' | 'patch' | 'reconcile' } |
bulkCases | run.cases_bulk_* | { cases: AuditBulkCaseEntry[], omittedCount?, summary: { affected, unchanged, statusFrom?, statusTo?, assigneeFrom?, assigneeTo? } } — hasta 20 entradas por caso al emitir; el resto en omittedCount |
runAggregates | run.closed, run.aborted | { countPassed, countFailed, countBlocked, countSkipped, countUntested, totalDurationMs, projectedStatus: 'passed' | 'failed' | 'aborted' } — run.aborted siempre reporta projectedStatus: 'aborted', aunque los conteos de casos por sí solos leerían 'passed'/'failed' |
Los snapshots de texto mayores a 240 grafemas se recortan al emitir; los objetos afectados marcan truncated: true según la convención de auditoría.
Formatos de entity.label en ejecuciones
Sección titulada «Formatos de entity.label en ejecuciones»entityType | Etiqueta canónica |
|---|---|
test_run | Nombre del run en el momento de emisión |
test_run_case | ${caseDisplayId} · ${titleSnapshot} — caseDisplayId es ${projectCode}-${caseNumber} (p. ej. ACME-1) |
test_run_case_step | ${caseDisplayId} · ${titleSnapshot} — ${stepActionSnapshot ?? `paso ${stepPosition}`} |
La lista puede añadir metadatos de enlace (runUlid, projectCode, caseDisplayId, titleSnapshot, runName) cuando la fila guardada no los tenía; los snapshots almacenados siempre prevalecen.
Metadatos run.* por acción
Sección titulada «Metadatos run.* por acción»| Acción | Claves relevantes en metadata |
|---|---|
run.created | caseCount, environment, defaultAssignee, milestone opcionales |
run.updated | changes (name, description, defaultAssigneeUlid, environment, milestoneUlid) |
run.closed | runAggregates (sustituye el status plano heredado) |
run.aborted | runAggregates (misma forma que run.closed; projectedStatus siempre es 'aborted') |
run.deleted | runAggregates, wasState (open | closed), closedAt (null si seguía abierto) |
run.cloned | caseCount, sourceRun, cloneAssignees, statusFilter (null si se clonaron todos los casos) |
run.case_added | added, cases (primeros 20), omittedCount opcional |
run.case_marked | caseDisplayId, titleSnapshot, changes (status), elapsedMs opcional |
run.case_step_marked | caseDisplayId, titleSnapshot, stepUlid, stepPosition, stepActionSnapshot, changes (status), caseStatus |
run.case_assigned | caseDisplayId, titleSnapshot, assigneeChange |
run.case_retried | caseDisplayId, titleSnapshot, changes (status → untested), previousElapsedMs opcional |
run.case_removed | caseDisplayId, titleSnapshot, statusAtRemoval |
run.cases_bulk_marked | bulkCases (por caso fromStatus / toStatus; summary.statusFrom / statusTo homogéneos cuando aplica) |
run.cases_bulk_assigned | bulkCases (por caso fromAssignee / toAssignee; resumen homogéneo cuando aplica) |
run.cases_bulk_retried | bulkCases (transiciones por caso; summary.unchanged para casos ya sin probar) |
run.cases_bulk_removed | bulkCases (por caso statusAtRemoval) |
run.case_step_actual_result_set | caseDisplayId, titleSnapshot, stepUlid, stepPosition, stepActionSnapshot, changes (actual_result de → a) |
run.result_attachment_committed | caseDisplayId, titleSnapshot, stepUlid, stepPosition, attachments; changes opcional cuando el texto de actual_result cambió en la misma petición |
run.result_attachment_deleted | Misma forma que run.result_attachment_committed |
run.reopened y run.reran_failed siguen siendo valores válidos del enum action para que las filas históricas sigan analizándose, pero ya no se emiten — Reopen se eliminó y rerun-failed se generalizó en run.cloned (POST .../runs/{runUlid}/clone).
Ejemplo — cierre con scorecard:
{ "action": "run.closed", "entityType": "test_run", "entityLabel": "Sprint 24", "metadata": { "runAggregates": { "countPassed": 7, "countFailed": 2, "countBlocked": 1, "countSkipped": 0, "countUntested": 0, "totalDurationMs": 935000, "projectedStatus": "failed" } }}Ejemplo — reasignación de caso:
{ "action": "run.case_assigned", "entityType": "test_run_case", "entityLabel": "ACME-1 · Flujo de login", "metadata": { "caseDisplayId": "ACME-1", "titleSnapshot": "Flujo de login", "assigneeChange": { "from": { "ulid": "01J…", "name": "María López" }, "to": { "ulid": "01J…", "name": "Pedro García" }, "via": "patch" } }}Mutaciones de ciclo de vida del run (abortar, clonar y eliminar)
Sección titulada «Mutaciones de ciclo de vida del run (abortar, clonar y eliminar)»Estos endpoints mutan runs y emiten las acciones de auditoría documentadas arriba. No forman parte de las rutas de listado de auditoría.
POST /api/v1/runs/{runUlid}/abort
Sección titulada «POST /api/v1/runs/{runUlid}/abort»- Rol: miembro, admin u owner (
viewer→403) - Cuerpo: ninguno
- Respuesta:
200con elRunResponseactualizado (state: "closed",abortedAtfijado al momento del abandono) - Errores:
409si el run no está actualmente abierto (ya cerrado o ya abortado) - Auditoría:
run.abortedconmetadata.runAggregates(projectedStatussiempre'aborted') - Los casos sin probar quedan sin probar — abortar no registra resultados de caso. Esto es terminal: un run abortado permanece congelado (las mutaciones de caso devuelven
409); no existe reapertura. Usa.../clone(abajo) para iniciar un run nuevo a partir de él.
POST /api/v1/projects/{projectId}/runs/{runUlid}/clone
Sección titulada «POST /api/v1/projects/{projectId}/runs/{runUlid}/clone»“Volver a ejecutar”: generaliza el antiguo endpoint rerun-failed (eliminado).
- Rol: miembro, admin u owner (
viewer→403) - Cuerpo:
{ "title": "string, obligatorio, máx 200", "cloneAssignees"?: boolean, "statusFilter"?: TestOutcomeStatus[] }—statusFilterselecciona qué casos del origen se copian; omitido o vacío significa todos. - Respuesta:
201con el nuevoRunResponse(state: "open") - Errores:
422sititleestá vacío ostatusFiltercontiene un estado desconocido - Auditoría:
run.clonedconmetadata.caseCount,metadata.sourceRun,metadata.cloneAssignees,metadata.statusFilter - Funciona sobre cualquier run origen cerrado — completado o abortado. Cuando
cloneAssigneesestrue, cada caso copiado conserva el asignado del caso origen; si no, todos los casos del nuevo run empiezan sin asignar.
DELETE /api/v1/runs/{runUlid}
Sección titulada «DELETE /api/v1/runs/{runUlid}»- Rol: solo admin u owner (
member/viewer→403) - Respuesta:
204 No Content - Auditoría:
run.deletedconmetadata.runAggregates,wasStateyclosedAtcapturados antes de la cascada. Tras el borrado,run_iden filas de auditoría (incluida esta) pasa anullpor cascada FK; el evento sigue visible en listados de toda la organización.
Mutaciones
Sección titulada «Mutaciones»Los endpoints de listado de auditoría son de solo lectura. Usa las rutas de run anteriores (y el resto de APIs del producto) para generar nuevas filas de auditoría.
Acciones de comentario
Sección titulada «Acciones de comentario»Estas acciones se emiten para los comentarios en hilos de las páginas de entidades, incluyendo respuestas (parentUlid presente) y fijado/desfijado. Las acciones commented, comment_updated y comment_deleted incluyen metadata.commentUlid y metadata.excerpt (primeros 240 caracteres del cuerpo del comentario); comment_pinned y comment_unpinned incluyen solo metadata.commentUlid (fijar no lleva extracto del cuerpo).
| Acción | Se emite cuando | Tipo de entidad |
|---|---|---|
defect.commented | Se agrega un comentario o respuesta a un defecto | defect |
defect.comment_updated | Se edita un comentario en un defecto | defect |
defect.comment_deleted | Se elimina un comentario en un defecto (una vez por cada respuesta en cascada también) | defect |
defect.comment_pinned | Se fija un comentario de primer nivel en un defecto | defect |
defect.comment_unpinned | Se desfija un comentario de primer nivel en un defecto | defect |
test_case.commented | Se agrega un comentario o respuesta a un caso de prueba | test_case |
test_case.comment_updated | Se edita un comentario en un caso de prueba | test_case |
test_case.comment_deleted | Se elimina un comentario en un caso de prueba (una vez por cada respuesta en cascada también) | test_case |
test_case.comment_pinned | Se fija un comentario de primer nivel en un caso de prueba | test_case |
test_case.comment_unpinned | Se desfija un comentario de primer nivel en un caso de prueba | test_case |
Ejemplo:
{ "action": "test_case.commented", "entityType": "test_case", "entityLabel": "TC-1 Login con credenciales válidas", "metadata": { "commentUlid": "01J…", "excerpt": "Reproducido en staging con sesión nueva." }}Gestión de tokens
Sección titulada «Gestión de tokens»Crea y revoca tokens de API en Autenticación API (en la app: /workspace/api-tokens).