API Documentation

Integra NextsID en tu sistema

API REST completa, documentada con OpenAPI 3.0. Valida conductores y vehículos desde tu TMS, ERP o cualquier aplicación.

Empieza en 3 pasos

1

Autentícate

POST /auth/login con tus credenciales devuelve un access token

2

Registra a la persona

POST /personas con el documento — devuelve el id que usa el estudio

3

Crea el estudio

POST /checks con ese id y el consentimiento confirmado

Request
# Copiá y pegá el bloque entero: cada paso deja lo que el siguiente necesita.

API=https://api.nexts.id

# 1. Autenticarse. El access token dura 15 min; el refresh, 7 días.
ACCESS_TOKEN=$(curl -sS -X POST "$API/api/v1/auth/login" \
  -H "Content-Type: application/json" \
  -d '{ "email": "integracion@tuempresa.com", "password": "..." }' \
  | jq -r .access_token)

# 2. Registrar a la persona. Devuelve el id que usa el estudio.
SUBJECT_ID=$(curl -sS -X POST "$API/api/v1/personas" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "document_type": "co_cedula",
    "document_number": "80123456",
    "country": "CO",
    "first_name": "Juan",
    "last_name": "Perez",
    "role": "conductor"
  }' | jq -r .id)

# 3. Crear el estudio sobre esa persona.
CHECK_ID=$(curl -sS -X POST "$API/api/v1/checks" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"subject_id\": \"$SUBJECT_ID\",
    \"subject_type\": \"persona\",
    \"consent_confirmed\": true,
    \"role\": \"conductor\"
  }" | jq -r .id)

# 4. ESPERAR — Y RAMIFICAR POR ESTADO. No alcanza con esperar
#    "sources_completed": si la ETAPA PREVIA encuentra algo crítico, el estudio
#    termina en pre_validation_failed y NUNCA llega a ese estado. Un lazo que
#    sólo espere el caso feliz gira para siempre y el integrador jamás ve el
#    hallazgo — que es justamente el peor resultado posible acá.
INTENTOS=0
while :; do
  ESTADO=$(curl -sS -w '\n%{http_code}' "$API/api/v1/checks/$CHECK_ID" \
    -H "Authorization: Bearer $ACCESS_TOKEN")
  CODIGO=$(printf '%s' "$ESTADO" | tail -1)
  CUERPO=$(printf '%s' "$ESTADO" | sed '$d')

  # El access token dura 900s. Si venció, renovalo con POST /auth/refresh.
  [ "$CODIGO" = "401" ] && { echo "token vencido: renová con /auth/refresh"; exit 1; }
  [ "$CODIGO" = "200" ] || { echo "error HTTP $CODIGO: $CUERPO"; exit 1; }

  case "$(printf '%s' "$CUERPO" | jq -r .stage)" in
    sources_completed)
      # LOS HALLAZGOS SE MIRAN ACÁ TAMBIÉN, no sólo cuando el estudio se
      # detiene en la etapa previa. Una coincidencia PEP real llega hasta acá:
      # su severidad es
      # "none" —porque es informativa y no mueve el veredicto— pero su
      # counts_as_finding es true. Si sólo se miran los hallazgos del camino
      # que falla, ese caso no se ve NUNCA. Y el paso 5 devuelve el puntaje,
      # que no trae la lista.
      printf '%s' "$CUERPO" | jq '.findings[] | select(.counts_as_finding)'
      break ;;
    pre_validation_failed)
      # HALLAZGO CRÍTICO EN LA PREVALIDACIÓN. Es un resultado, no un error:
      # el estudio se detuvo porque encontró algo. Mostralo.
      printf '%s' "$CUERPO" | jq '.findings[] | select(.counts_as_finding)'
      exit 2 ;;
    *)
      : ;;   # pending, pre_validating, running, completed, partial: seguir esperando
  esac

  INTENTOS=$((INTENTOS + 1))
  [ "$INTENTOS" -gt 100 ] && { echo "el estudio no terminó en ~5 min"; exit 1; }
  sleep 3
done

# 5. Puntuar el estudio. ESTE PASO ES OBLIGATORIO: el paso 4 devuelve el estudio
#    con sus fuentes, no un veredicto. Sin este POST no hay score ni PDF.
curl -sS -X POST "$API/api/v1/checks/$CHECK_ID/score" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

# 6. ESPERAR el certificado. El PDF se genera en una tarea aparte DESPUÉS de que
#    el paso 5 respondió, así que pedirlo enseguida devuelve 404 —no un error de
#    permisos: todavía no existe.
#
#    Ojo: /report NO devuelve el PDF, devuelve un JSON con una URL firmada.
INTENTOS=0
while :; do
  RESP=$(curl -sS -w '\n%{http_code}' "$API/api/v1/checks/$CHECK_ID/report" \
    -H "Authorization: Bearer $ACCESS_TOKEN")
  CODIGO=$(printf '%s' "$RESP" | tail -1)
  CUERPO=$(printf '%s' "$RESP" | sed '$d')

  [ "$CODIGO" = "200" ] && break
  [ "$CODIGO" = "401" ] && { echo "token vencido: renová con /auth/refresh"; exit 1; }
  [ "$CODIGO" = "404" ] || { echo "error HTTP $CODIGO: $CUERPO"; exit 1; }

  INTENTOS=$((INTENTOS + 1))
  [ "$INTENTOS" -gt 60 ] && { echo "el PDF no se generó en ~3 min"; exit 1; }
  sleep 3
done

# Y el PDF, desde la URL firmada.
curl -sS -o certificado.pdf "$(printf '%s' "$CUERPO" | jq -r .download_url)"
Response 200 OK
{
  "background_check_id": "019fea9a-eb20-7f21-bb50-e539b48b6573",
  "final_verdict": "aprobado",
  "verdict_reason": "Score: 1.00, Unavailable sources: 0, Unfinished sources: 0",
  "composite_score": 1.0,
  "category_scores": {
    "criminal_record":          { "score": 1.0, "worst_severity": "none", "finding_count": 0 },
    "legal_background":         { "score": 1.0, "worst_severity": "none", "finding_count": 0 },
    "international_background": { "score": 1.0, "worst_severity": "none", "finding_count": 0 },
    "personal_identity":        { "score": 1.0, "worst_severity": "none", "finding_count": 0 }
  },
  "unavailable_sources": [],
  "business_rules_triggered": [],
  "scored_at": "2026-08-10T14:23:15Z"
}

No filtres los hallazgos por severidad

Quién decide si un hallazgo cuenta no es su severidad, sino el campo counts_as_finding, que viaja en cada hallazgo. Leer la severidad sola miente en las dos direcciones:

  • Un resultado limpio puede traer un hallazgo. Ocho fuentes colombianas expresan “sin antecedentes” emitiendo un hallazgo con severidad none — Procuraduría emite literalmente “No registra antecedentes”. Contar la lista pinta de rojo una consulta limpia.
  • Y una coincidencia REAL puede tener severidadnone. Es el caso de PEP (Personas Expuestas Políticamente): la coincidencia es cierta y es informativa, no mueve el veredicto, pero counts_as_finding es true. Si filtrás por severidad, la escondés.

Por eso el ejemplo de arriba imprime los hallazgos también cuando el estudio termina bien, y no sólo cuando se detiene en la etapa previa: un estudio aprobado puede traer un hallazgo que tenés que mostrar.

Autenticación JWT

Login con credenciales. El access token dura 15 minutos y se renueva con el refresh token.

Consulta del resultado

Un estudio consulta 16 fuentes, así que no responde en el mismo request: se consulta su estado por id.

JSON everywhere

Request y response en JSON. Swagger UI en /swagger-ui con el listado de endpoints.

Rate limiting

Protección inteligente con límites por tenant. Headers X-RateLimit-* en cada response.

Endpoints principales

POST/api/v1/checks
GET/api/v1/checks/:id
POST/api/v1/batches
GET/api/v1/checks
POST/api/v1/vehicles
GET/api/v1/vehicles
GET/api/v1/checks/{id}/score
GET/api/v1/audit

Swagger UI interactivo: Explora y prueba todos los endpoints en /swagger-ui después de iniciar sesión.

¿Listo para integrar?

Solicitá tus credenciales de integración y empezá a validar conductores desde tu sistema.