> ## Documentation Index
> Fetch the complete documentation index at: https://omniloy.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Operación, Errores y Seguridad

> Códigos de error, estrategias de reintento, rate limiting y mejores prácticas de seguridad para la API Codify de SofIA

## Manejo de errores

### Códigos de error HTTP

| Código  | Significado           | Causa común                                                                   | Acción recomendada                                                  |
| ------- | --------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| **400** | Bad Request           | Falta el campo requerido `medical_note` o formato de petición inválido        | Asegurar que el campo `medical_note` está proporcionado (requerido) |
| **401** | Unauthorized          | Token inválido o expirado                                                     | Verifique su token Bearer y que esté activo                         |
| **413** | Payload Too Large     | `medical_note` excede 50KB o `pdf_file` excede 5MB decodificado               | Reducir tamaño de entrada o dividir contenido                       |
| **422** | Unprocessable Entity  | Estructura PDF inválida, datos base64 malformados, mime\_type inválido        | Validar estructura y codificación del archivo PDF                   |
| **429** | Too Many Requests     | Límite de velocidad excedido                                                  | Espera y reintenta con backoff exponencial                          |
| **500** | Internal Server Error | Error inesperado del servidor, problemas de base de datos, mala configuración | Reintenta con backoff exponencial; si persiste, reporta el `run_id` |
| **503** | Service Unavailable   | Servicio de IA temporalmente no disponible                                    | Reintenta después de unos segundos                                  |
| **504** | Gateway Timeout       | La petición excedió el límite de procesamiento de 10 minutos                  | Reducir tamaño de entrada o dividir contenido                       |

### Formato de respuestas de error

Todos los errores siguen esta estructura consistente:

```json theme={null}
{
  "success": false,
  "error": "Bad Request",
  "details": "medical_note es requerido"
}
```

### Campos de error

| Campo     | Descripción                                              |
| --------- | -------------------------------------------------------- |
| `success` | Siempre `false` para respuestas de error                 |
| `error`   | Nombre/tipo de error (ej: "Bad Request", "Unauthorized") |
| `details` | Mensaje de error detallado explicando qué salió mal      |

***

## Estrategia de reintentos

### ¿Cuándo reintentar?

**Sí, reintentar:**

* Errores **5xx** (500, 502, 503, 504)
* Error **429** (Too Many Requests)
* Errores de red/timeout

**No reintentar:**

* Errores **4xx** excepto 429 (son errores de cliente)
* Error 401 (token inválido)
* Error 422 (esquema inválido)

### Backoff exponencial

```
Intento 1: inmediato
Intento 2: espera 1 segundo
Intento 3: espera 2 segundos
Intento 4: espera 4 segundos
Intento 5: espera 8 segundos
...
```

### Implementación

<CodeGroup>
  ```python Python theme={null}
  import time
  import requests
  from requests.exceptions import RequestException

  def codify_with_retry(payload, max_retries=3):
      """Llama a la API Codify con reintentos exponenciales"""
      url = "https://{tu-endpoint}/v1/codify"
      headers = {
          "Authorization": "Bearer TU_TOKEN",
          "Content-Type": "application/json"
      }

      for attempt in range(max_retries):
          try:
              response = requests.post(
                  url=url,
                  json=payload,
                  headers=headers,
                  timeout=600  # Timeout de 10 minutos para Codify
              )

              # Si es 429 o 5xx, reintentar
              if response.status_code in [429, 500, 502, 503, 504]:
                  if attempt < max_retries - 1:
                      wait_time = 2 ** attempt
                      print(f"Reintentando en {wait_time}s...")
                      time.sleep(wait_time)
                      continue

              # Para otros errores, lanzar excepción
              response.raise_for_status()
              return response.json()

          except RequestException as e:
              if attempt < max_retries - 1:
                  wait_time = 2 ** attempt
                  print(f"Error de red, reintentando en {wait_time}s...")
                  time.sleep(wait_time)
              else:
                  raise

      raise Exception("Máximo de reintentos alcanzado")

  # Uso
  try:
      result = codify_with_retry({
          "medical_note": "Paciente con Diabetes tipo 2...",
          "model": "balanced"
      })
      print(f"Éxito! Códigos: {len(result['final_code_assessments'])}")
  except Exception as e:
      print(f"Error: {e}")
  ```

  ```javascript JavaScript theme={null}
  async function codifyWithRetry(payload, maxRetries = 3) {
    const url = 'https://{tu-endpoint}/v1/codify';
    const headers = {
      'Authorization': 'Bearer TU_TOKEN',
      'Content-Type': 'application/json'
    };

    for (let attempt = 0; attempt < maxRetries; attempt++) {
      try {
        const response = await axios.post(url, payload, {
          headers,
          timeout: 600000  // Timeout de 10 minutos para Codify
        });

        return response.data;

      } catch (error) {
        const status = error.response?.status;

        // Reintentar en 429 y 5xx
        if ([429, 500, 502, 503, 504].includes(status)) {
          if (attempt < maxRetries - 1) {
            const waitTime = Math.pow(2, attempt) * 1000;
            console.log(`Reintentando en ${waitTime/1000}s...`);
            await new Promise(resolve => setTimeout(resolve, waitTime));
            continue;
          }
        }

        // Para otros errores, lanzar
        throw error;
      }
    }

    throw new Error('Máximo de reintentos alcanzado');
  }

  // Uso
  try {
    const result = await codifyWithRetry({
      medical_note: 'Paciente con Diabetes tipo 2...',
      model: 'balanced'
    });
    console.log(`Éxito! Códigos: ${result.final_code_assessments.length}`);
  } catch (error) {
    console.error('Error:', error.response?.data || error.message);
  }
  ```
</CodeGroup>

***

## Seguimiento y auditoría

### Uso de run\_id para depuración

Cada respuesta incluye un `run_id` único que debe almacenar para seguimiento y depuración:

```python theme={null}
def save_codification_result(patient_id, result):
    """Guardar resultado de codificación con run_id para seguimiento"""
    database.insert({
        'patient_id': patient_id,
        'run_id': result['run_id'],
        'codes': result['final_code_assessments'],
        'discarded_codes': result['discarded_code_assessments'],
        'timestamp': datetime.now(),
        'processed': True
    })
    
    print(f"✅ Codificación guardada (run_id: {result['run_id']})")
    return result['run_id']

# Uso
result = codify_with_retry({"medical_note": "..."})
run_id = save_codification_result("pt_456", result)
```

### Deduplicación por hash de contenido

Para evitar procesar la misma nota múltiples veces, use hash de contenido:

```python theme={null}
import hashlib
import json

def get_content_hash(medical_note):
    """Generar hash del contenido de la nota médica"""
    return hashlib.sha256(medical_note.encode()).hexdigest()

def codify_with_deduplication(medical_note):
    """Procesar nota solo si no ha sido procesada anteriormente"""
    content_hash = get_content_hash(medical_note)
    
    # Verificar si ya se procesó
    existing = database.query(
        "SELECT * FROM codifications WHERE content_hash = ?",
        content_hash
    )
    
    if existing:
        print(f"✅ Ya procesado (run_id: {existing['run_id']})")
        return existing['result']
    
    # Procesar nueva nota
    result = codify_with_retry({"medical_note": medical_note})
    
    # Guardar con hash
    database.insert({
        'content_hash': content_hash,
        'run_id': result['run_id'],
        'result': json.dumps(result),
        'processed_at': datetime.now()
    })
    
    return result
```

### Headers opcionales de seguimiento

Use los headers `doctor` y `patient` para mejorar las pistas de auditoría:

```python theme={null}
def codify_with_tracking(medical_note, doctor_id, patient_id):
    """Codificar con información completa de seguimiento"""
    url = "https://{tu-endpoint}/v1/codify"
    headers = {
        "Authorization": "Bearer TU_TOKEN",
        "Content-Type": "application/json",
        "x-doctor": doctor_id,
        "x-patient": patient_id
    }
    
    payload = {"medical_note": medical_note}
    
    response = requests.post(url, json=payload, headers=headers)
    result = response.json()
    
    # Registrar con contexto completo
    logger.info(f"Nota codificada para paciente {patient_id} por doctor {doctor_id}, run_id: {result['run_id']}")
    
    return result
```

***

## Próximos pasos

<Columns cols={2}>
  <Card title="Inicio rápido" href="/sofia/es/api/quickstart" icon="book">
    Su primera llamada
  </Card>

  <Card title="Estructura de petición" href="/sofia/es/api/request-response" icon="clipboard">
    Referencia completa de campos
  </Card>

  <Card title="Ejemplos" href="/sofia/es/api/examples" icon="lightbulb">
    Casos de uso reales
  </Card>

  <Card title="Referencia OpenAPI" href="/api-reference/codify-clinical-text-into-a-json-structure-you-define" icon="book">
    Especificación completa
  </Card>
</Columns>
