{
  "openapi": "3.1.0",
  "info": {
    "title": "Hablame API",
    "version": "6.0.0",
    "summary": "API REST del operador: consulta de números, acortador de URLs, texto a voz, llamadas, cuenta y catálogos.",
    "description": "La API v6 de Hablame se autentica con tokens Bearer, devuelve cada respuesta dentro de la envoltura uniforme `{ success, data | error, meta }` y comunica los límites de uso con las cabeceras estándar `RateLimit-*`.\n\nEsta especificación se genera a partir de la misma fuente que la documentación publicada en el portal.\n\nCada operación declara su límite y su alcance por duplicado: `x-limit`/`x-access` son los nombres canónicos, y `x-tps`/`x-scope` se mantienen con la forma que tenían en la especificación anterior para no romper las herramientas que ya los leían.",
    "contact": {
      "name": "Soporte a desarrolladores Hablame",
      "email": "developers@hablame.co",
      "url": "https://developers.hablame.co/docs"
    }
  },
  "servers": [
    {
      "url": "https://developers.hablame.co",
      "description": "Producción"
    },
    {
      "url": "https://qa-developers.hablame.co",
      "description": "QA / staging"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Utilidades",
      "description": "Endpoints de diagnóstico, disponibles para cualquier API key. Sirven para verificar que tus credenciales quedaron bien conectadas."
    },
    {
      "name": "Herramientas",
      "description": "Catálogos de propósito general que puedes consumir desde tu aplicación. Disponibles para cualquier API key."
    },
    {
      "name": "Number Insight",
      "description": "Resuelve un número telefónico: país, tipo de línea, operador actual (en Colombia siguiendo portabilidad) y área. Uno a uno o por lote."
    },
    {
      "name": "Acortador de URLs",
      "description": "Crea y administra enlaces cortos en tus dominios y consulta su analítica de clics."
    },
    {
      "name": "Text-to-Speech",
      "description": "Convierte texto en audio con voces naturales, reutiliza automáticamente un audio idéntico ya generado y consulta el estado de cada solicitud."
    },
    {
      "name": "Call Blasting",
      "description": "Llamadas de voz salientes con audio pregrabado o texto a voz, con reintentos configurables y estado por intento."
    },
    {
      "name": "Cuenta",
      "description": "Resumen de facturación de la organización: saldo o cupo, topes de gasto y movimientos. Información financiera sensible."
    },
    {
      "name": "Organización",
      "description": "Perfil de la organización, centros de costo y usuarios. Incluye datos personales."
    },
    {
      "name": "Plataforma",
      "description": "Estado operativo de los servicios y catálogo de lo que tu cuenta tiene habilitado."
    }
  ],
  "paths": {
    "/api/v6/utilities/ping": {
      "get": {
        "operationId": "utilities.ping",
        "tags": [
          "Utilidades"
        ],
        "summary": "Ping",
        "description": "Devuelve el estado del llamante: confirma que la API key es válida y muestra los datos clave que la API conoce de quien llama.\n\nÚsalo como prueba de humo después de configurar tus credenciales. Un `200` significa que tu integración está lista para llamar al resto de los endpoints.\n\nEl detalle de la organización, el centro de costo y los servicios habilitados **no** viaja en esta respuesta: vive en `/api/v6/account/organization`, `/api/v6/account/cost-centers` y `/api/v6/catalog/services`.\n\n## Alcance y límites\n\n- Acceso: Cualquier API key válida.\n- Límite: 20 solicitudes por minuto.",
        "x-tps": {
          "limit": 20,
          "period": 60,
          "endpointId": "utilities.ping"
        },
        "x-scope": {
          "type": "universal"
        },
        "x-limit": {
          "requests": 20,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "universal"
        },
        "responses": {
          "200": {
            "description": "La key es válida.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PingSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "pong": true,
                    "apiVersion": "v6",
                    "client": {
                      "ip": "203.0.113.45"
                    }
                  },
                  "meta": {
                    "requestId": "b9b1704baffab21150213c02fd853975",
                    "timestamp": "2026-06-04T19:43:59+00:00",
                    "responseTimeMs": 4.24
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes. El `error.code` indica el motivo concreto.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthCostCenterDisabled": {
                    "$ref": "#/components/examples/AuthCostCenterDisabled"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Las credenciales son válidas, pero la organización no puede ejecutar la solicitud.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AccountNotActive": {
                    "$ref": "#/components/examples/AccountNotActive"
                  },
                  "AccountBlocked": {
                    "$ref": "#/components/examples/AccountBlocked"
                  },
                  "AccountReadOnly": {
                    "$ref": "#/components/examples/AccountReadOnly"
                  },
                  "AccountConfigNotFound": {
                    "$ref": "#/components/examples/AccountConfigNotFound"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes. Espera lo que indique `Retry-After`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateDdosExceeded": {
                    "$ref": "#/components/examples/RateDdosExceeded"
                  },
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Servicio temporalmente no disponible. Reintenta después del tiempo indicado en `Retry-After`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "InfraDbConnectionError": {
                    "$ref": "#/components/examples/InfraDbConnectionError"
                  },
                  "InfraCacheUnavailable": {
                    "$ref": "#/components/examples/InfraCacheUnavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Se agotó el tiempo de espera al procesar la solicitud. Es seguro reintentar.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "InfraDbQueryTimeout": {
                    "$ref": "#/components/examples/InfraDbQueryTimeout"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/tools/countries": {
      "get": {
        "operationId": "tools.countries.list",
        "tags": [
          "Herramientas"
        ],
        "summary": "Listar países",
        "description": "Devuelve el catálogo completo de países que la API conoce, con su código ISO, código telefónico, MCC móvil y nombres en inglés y español. Úsalo para renderizar selectores de país, autodetectar el país a partir de un prefijo o resolver MCCs.\n\nEl catálogo cambia raras veces. La respuesta incluye `Cache-Control: public, max-age=3600` para que tu cliente y cualquier CDN intermedio amortigüen el tráfico.\n\nLos nombres se ofrecen solo en inglés (`en`) y español (`es`). Cualquier otro valor de `lang` cae a inglés.\n\n## Alcance y límites\n\n- Acceso: Cualquier API key válida.\n- Límite: 60 solicitudes por minuto.",
        "x-tps": {
          "limit": 60,
          "period": 60,
          "endpointId": "tools.countries.list"
        },
        "x-scope": {
          "type": "universal"
        },
        "x-limit": {
          "requests": 60,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "universal"
        },
        "parameters": [
          {
            "name": "lang",
            "in": "query",
            "required": false,
            "description": "Código ISO-639-1. Cuando se especifica, cada país devuelve `name` plano en ese idioma en vez del mapa completo `names`.",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "es"
              ],
              "example": "es",
              "description": "Código ISO-639-1. Cuando se especifica, cada país devuelve `name` plano en ese idioma en vez del mapa completo `names`."
            }
          },
          {
            "name": "callingCode",
            "in": "query",
            "required": false,
            "description": "Filtra países por código telefónico E.164 (por ejemplo `57` para Colombia). Acepta un `+` inicial opcional.",
            "schema": {
              "type": "string",
              "example": "57",
              "description": "Filtra países por código telefónico E.164 (por ejemplo `57` para Colombia). Acepta un `+` inicial opcional."
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Búsqueda por subcadena, sin distinguir mayúsculas, sobre el nombre del país en el idioma solicitado. Mínimo 2 caracteres.",
            "schema": {
              "type": "string",
              "example": "col",
              "minLength": 2,
              "description": "Búsqueda por subcadena, sin distinguir mayúsculas, sobre el nombre del país en el idioma solicitado. Mínimo 2 caracteres."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Catálogo de países.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CountriesListSuccess"
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "code": "CO",
                      "code3": "COL",
                      "callingCode": "57",
                      "mcc": "732",
                      "flag": "https://flagcdn.com/co.svg",
                      "names": {
                        "en": "Colombia",
                        "es": "Colombia"
                      }
                    },
                    {
                      "code": "MX",
                      "code3": "MEX",
                      "callingCode": "52",
                      "mcc": "334",
                      "flag": "https://flagcdn.com/mx.svg",
                      "names": {
                        "en": "Mexico",
                        "es": "México"
                      }
                    }
                  ],
                  "meta": {
                    "requestId": "b9b1704baffab21150213c02fd853975",
                    "timestamp": "2026-05-23T02:00:00+00:00",
                    "responseTimeMs": 4.12
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateDdosExceeded": {
                    "$ref": "#/components/examples/RateDdosExceeded"
                  },
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Servicio temporalmente no disponible.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/tools/countries/{code}": {
      "get": {
        "operationId": "tools.countries.get",
        "tags": [
          "Herramientas"
        ],
        "summary": "Obtener un país",
        "description": "Devuelve los datos de un país por su código ISO 3166-1 alpha-2 (2 letras, sin distinguir mayúsculas).\n\n## Alcance y límites\n\n- Acceso: Cualquier API key válida.\n- Límite: 60 solicitudes por minuto.",
        "x-tps": {
          "limit": 60,
          "period": 60,
          "endpointId": "tools.countries.get"
        },
        "x-scope": {
          "type": "universal"
        },
        "x-limit": {
          "requests": 60,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "universal"
        },
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "description": "Código ISO 3166-1 alpha-2 del país (`CO`, `MX`, `US`...). No distingue mayúsculas.",
            "schema": {
              "type": "string",
              "example": "CO",
              "minLength": 2,
              "maxLength": 2,
              "description": "Código ISO 3166-1 alpha-2 del país (`CO`, `MX`, `US`...). No distingue mayúsculas."
            }
          },
          {
            "name": "lang",
            "in": "query",
            "required": false,
            "description": "Código ISO-639-1. Devuelve `name` plano en ese idioma en vez del mapa completo `names`.",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "es"
              ],
              "example": "es",
              "description": "Código ISO-639-1. Devuelve `name` plano en ese idioma en vez del mapa completo `names`."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Datos del país.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CountrySingleSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "code": "CO",
                    "code3": "COL",
                    "callingCode": "57",
                    "mcc": "732",
                    "flag": "https://flagcdn.com/co.svg",
                    "name": "Colombia"
                  },
                  "meta": {
                    "requestId": "b9b1704baffab21150213c02fd853975",
                    "timestamp": "2026-05-23T02:00:00+00:00",
                    "responseTimeMs": 2.1
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No existe un país con ese código.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "CountryNotFound": {
                    "$ref": "#/components/examples/CountryNotFound"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/numberinsight/{number}": {
      "get": {
        "operationId": "numberinsight.lookup",
        "tags": [
          "Number Insight"
        ],
        "summary": "Consulta de un número",
        "description": "Resuelve un número telefónico: país, tipo de línea, operador (en Colombia siguiendo portabilidad), área y, para Colombia, el NRN de enrutamiento.\n\nLa respuesta tiene la misma estructura para Colombia y para el resto del mundo: los campos que no aplican vienen en `null`. Así tu cliente no necesita dos ramas de parseo.\n\n## Alcance y límites\n\n- Acceso: Cualquier API key válida.\n- Límite: 60 solicitudes por minuto.\n- Admite `Idempotency-Key`.",
        "x-tps": {
          "limit": 60,
          "period": 60,
          "endpointId": "numberinsight.lookup"
        },
        "x-scope": {
          "type": "universal"
        },
        "x-limit": {
          "requests": 60,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "universal"
        },
        "parameters": [
          {
            "name": "number",
            "in": "path",
            "required": true,
            "description": "Número en E.164 (`+5491123456789`), E.164 sin `+` (`5491123456789`) o formato nacional colombiano (10 dígitos).",
            "schema": {
              "type": "string",
              "example": "3001234567",
              "minLength": 6,
              "maxLength": 32,
              "description": "Número en E.164 (`+5491123456789`), E.164 sin `+` (`5491123456789`) o formato nacional colombiano (10 dígitos)."
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Llave de idempotencia opcional (1 a 255 caracteres de `A-Za-z0-9_-`). Repetir la misma operación con la misma llave devuelve la respuesta original sin volver a ejecutarla. Ver la [guía de idempotencia](/docs/idempotency).",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]+$",
              "minLength": 1,
              "maxLength": 255,
              "description": "Llave de idempotencia opcional (1 a 255 caracteres de `A-Za-z0-9_-`). Repetir la misma operación con la misma llave devuelve la respuesta original sin volver a ejecutarla. Ver la [guía de idempotencia](/docs/idempotency)."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Número resuelto. Los campos no aplicables vienen en `null`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Idempotency-Status": {
                "$ref": "#/components/headers/Idempotency-Status"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/Idempotency-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NumberInsightSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "phoneNumber": {
                      "e164": "+573001234567",
                      "national": "3001234567",
                      "raw": "3001234567"
                    },
                    "valid": true,
                    "country": {
                      "iso2": "CO",
                      "callingCode": "57",
                      "name": "Colombia",
                      "mcc": "732"
                    },
                    "lineType": "mobile",
                    "numberType": "mobile",
                    "ported": true,
                    "operator": {
                      "name": "Tigo",
                      "brand": "Tigo",
                      "mnc": "103",
                      "nrn": "103"
                    },
                    "area": null,
                    "timezone": null,
                    "portability": {
                      "donorOperator": {
                        "name": "Claro",
                        "brand": "Claro"
                      },
                      "initialOperator": {
                        "name": "Claro",
                        "brand": "Claro"
                      },
                      "activatedAt": "2024-08-12",
                      "processedAt": "2024-08-11T14:32:00Z",
                      "sourceDate": "2024-08-11"
                    },
                    "zone": {
                      "id": "103",
                      "name": "Tigo"
                    }
                  },
                  "meta": {
                    "requestId": "8f0c0e2a4b1d4c8fae2b7a91e0c5d3f6",
                    "timestamp": "2026-06-18T09:00:00+00:00",
                    "responseTimeMs": 2.1
                  }
                }
              }
            }
          },
          "400": {
            "description": "Llave de idempotencia inválida.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "IdempotencyKeyInvalid": {
                    "$ref": "#/components/examples/IdempotencyKeyInvalid"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  }
                }
              }
            }
          },
          "403": {
            "description": "La cuenta no puede ejecutar esta consulta.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AccountNotActive": {
                    "$ref": "#/components/examples/AccountNotActive"
                  },
                  "AccountBlocked": {
                    "$ref": "#/components/examples/AccountBlocked"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Una petición con la misma llave de idempotencia sigue en proceso.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "IdempotencyInProgress": {
                    "$ref": "#/components/examples/IdempotencyInProgress"
                  }
                }
              }
            }
          },
          "422": {
            "description": "La llave de idempotencia se reusó con un cuerpo distinto.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "IdempotencyKeyReused": {
                    "$ref": "#/components/examples/IdempotencyKeyReused"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          },
          "503": {
            "description": "El servicio de consulta se está actualizando. Reintenta en unos segundos.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "InfraCacheUnavailable": {
                    "$ref": "#/components/examples/InfraCacheUnavailable"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/numberinsight/batch": {
      "post": {
        "operationId": "numberinsight.batch",
        "tags": [
          "Number Insight"
        ],
        "summary": "Consulta por lote",
        "description": "Procesa muchos números en una sola solicitud. Hay dos modalidades y se eligen con el `Content-Type`.\n\nCuando el trabajo asíncrono termina se dispara el evento `numberinsight.batch.completed` y el resultado en NDJSON queda disponible en una URL de descarga temporal que entrega el endpoint de estado.\n\n## Alcance y límites\n\n- Acceso: Cualquier API key válida.\n- Límite: 10 solicitudes por minuto.\n- Modalidad síncrona: hasta 500 números por solicitud.\n- Modalidad asíncrona: archivo de texto plano UTF-8, una línea por número, cada línea de máximo 64 caracteres.\n- El resultado de un trabajo queda disponible durante unos 90 días desde su creación.\n- Admite `Idempotency-Key`.",
        "x-tps": {
          "limit": 10,
          "period": 60,
          "endpointId": "numberinsight.batch"
        },
        "x-scope": {
          "type": "universal"
        },
        "x-limit": {
          "requests": 10,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "universal"
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Llave de idempotencia opcional (1 a 255 caracteres de `A-Za-z0-9_-`). Repetir la misma operación con la misma llave devuelve la respuesta original sin volver a ejecutarla. Ver la [guía de idempotencia](/docs/idempotency).",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]+$",
              "minLength": 1,
              "maxLength": 255,
              "description": "Llave de idempotencia opcional (1 a 255 caracteres de `A-Za-z0-9_-`). Repetir la misma operación con la misma llave devuelve la respuesta original sin volver a ejecutarla. Ver la [guía de idempotencia](/docs/idempotency)."
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "numbers": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "maxItems": 500,
                    "description": "Números a resolver. Mismo formato que la consulta uno a uno."
                  }
                },
                "required": [
                  "numbers"
                ]
              },
              "example": {
                "numbers": [
                  "3001234567",
                  "6017430000",
                  "+5491123456789"
                ]
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "Archivo de texto plano UTF-8, un número por línea (cada línea de máximo 64 caracteres)."
                  },
                  "webhookUrl": {
                    "type": "string",
                    "format": "uri",
                    "description": "URL opcional para recibir el evento `numberinsight.batch.completed` cuando el trabajo termine."
                  }
                },
                "required": [
                  "file"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Modalidad síncrona: resuelto en línea.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Idempotency-Status": {
                "$ref": "#/components/headers/Idempotency-Status"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/Idempotency-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NumberInsightBatchSyncSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "results": [
                      {
                        "phoneNumber": {
                          "e164": "+573001234567",
                          "national": "3001234567",
                          "raw": "3001234567"
                        },
                        "valid": true,
                        "country": {
                          "iso2": "CO",
                          "callingCode": "57",
                          "name": "Colombia",
                          "mcc": "732"
                        },
                        "lineType": "mobile",
                        "numberType": "mobile",
                        "ported": false,
                        "operator": {
                          "name": "Tigo",
                          "brand": "Tigo",
                          "mnc": "103",
                          "nrn": "103"
                        },
                        "area": null,
                        "timezone": null,
                        "portability": null,
                        "zone": {
                          "id": "103",
                          "name": "Tigo"
                        }
                      }
                    ]
                  },
                  "meta": {
                    "requestId": "b9b1704baffab21150213c02fd853975",
                    "timestamp": "2026-06-02T19:43:59+00:00",
                    "responseTimeMs": 4.1
                  }
                }
              }
            }
          },
          "202": {
            "description": "Modalidad asíncrona: trabajo aceptado y en cola.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Idempotency-Status": {
                "$ref": "#/components/headers/Idempotency-Status"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/Idempotency-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NumberInsightBatchQueuedSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "jobId": "4e2c8b91-3f12-4ad6-9b91-09e8e9c5e7a1",
                    "status": "queued",
                    "count": 12450,
                    "expiresAt": "2026-09-15T14:00:00Z"
                  },
                  "meta": {
                    "requestId": "b9b1704baffab21150213c02fd853975",
                    "timestamp": "2026-06-02T19:43:59+00:00",
                    "responseTimeMs": 4.1
                  }
                }
              }
            }
          },
          "400": {
            "description": "Falta `numbers` en la modalidad síncrona, el cuerpo es inválido, o la llave de idempotencia no cumple el formato.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "IdempotencyKeyInvalid": {
                    "$ref": "#/components/examples/IdempotencyKeyInvalid"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Una petición con la misma llave de idempotencia sigue en proceso.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "IdempotencyInProgress": {
                    "$ref": "#/components/examples/IdempotencyInProgress"
                  }
                }
              }
            }
          },
          "413": {
            "description": "El lote síncrono superó los 500 números: usa la modalidad asíncrona.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "El archivo asíncrono es inválido (no es texto, tiene líneas de más de 64 caracteres o está vacío), o la llave de idempotencia se reusó con un cuerpo distinto.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "IdempotencyKeyReused": {
                    "$ref": "#/components/examples/IdempotencyKeyReused"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          },
          "503": {
            "description": "El servicio de consulta se está actualizando. Reintenta en unos segundos.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "InfraCacheUnavailable": {
                    "$ref": "#/components/examples/InfraCacheUnavailable"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/numberinsight/batch/{jobId}": {
      "get": {
        "operationId": "numberinsight.batch.status",
        "tags": [
          "Number Insight"
        ],
        "summary": "Estado de un lote",
        "description": "Devuelve el estado de un trabajo creado con `POST /api/v6/numberinsight/batch`. Cuando `status` es `done`, la respuesta incluye `download.url`, una URL de descarga temporal válida por 60 minutos con el NDJSON resultante.\n\nEl trabajo queda disponible unos 90 días desde su creación. Después responde `404`.\n\n## Alcance y límites\n\n- Acceso: Cualquier API key válida.\n- Límite: 60 solicitudes por minuto.",
        "x-tps": {
          "limit": 60,
          "period": 60,
          "endpointId": "numberinsight.batch.status"
        },
        "x-scope": {
          "type": "universal"
        },
        "x-limit": {
          "requests": 60,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "universal"
        },
        "parameters": [
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "description": "UUID devuelto al crear el trabajo.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "4e2c8b91-3f12-4ad6-9b91-09e8e9c5e7a1",
              "description": "UUID devuelto al crear el trabajo."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Estado del trabajo.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NumberInsightBatchJobSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "jobId": "4e2c8b91-3f12-4ad6-9b91-09e8e9c5e7a1",
                    "status": "done",
                    "count": 12450,
                    "processed": 12450,
                    "createdAt": "2026-06-18T09:00:00Z",
                    "expiresAt": "2026-09-15T14:00:00Z",
                    "download": {
                      "url": "https://developers.hablame.co/api/v6/numberinsight/downloads/4e2c8b91.ndjson?expires=1789000000&signature=...",
                      "format": "ndjson"
                    }
                  },
                  "meta": {
                    "requestId": "b9b1704baffab21150213c02fd853975",
                    "timestamp": "2026-06-02T19:43:59+00:00",
                    "responseTimeMs": 4.1
                  }
                }
              }
            }
          },
          "400": {
            "description": "`jobId` vacío.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Trabajo no encontrado, de otra cuenta, vencido o ya purgado.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/urlshortener/links": {
      "post": {
        "operationId": "urlshortener.create",
        "tags": [
          "Acortador de URLs"
        ],
        "summary": "Crear un enlace corto",
        "description": "Crea un enlace corto en uno de tus dominios. El código se genera automáticamente (base36, 7 caracteres) salvo que envíes un `alias` propio. Los códigos son únicos **por dominio**: el mismo alias puede existir en dos dominios distintos.\n\n## Alcance y límites\n\n- Acceso: API key con el servicio `urlshortener` habilitado.\n- Límite: 35 solicitudes por segundo.\n- La URL de destino admite hasta 2048 caracteres.\n- Se rechazan destinos que apunten a hosts privados o reservados.\n- Admite `Idempotency-Key`.",
        "x-tps": {
          "limit": 35,
          "period": 1,
          "endpointId": "urlshortener.create"
        },
        "x-scope": {
          "type": "service",
          "value": "urlshortener"
        },
        "x-limit": {
          "requests": 35,
          "windowSeconds": 1
        },
        "x-access": {
          "kind": "service",
          "value": "urlshortener"
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Llave de idempotencia opcional (1 a 255 caracteres de `A-Za-z0-9_-`). Repetir la misma operación con la misma llave devuelve la respuesta original sin volver a ejecutarla. Ver la [guía de idempotencia](/docs/idempotency).",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]+$",
              "minLength": 1,
              "maxLength": 255,
              "description": "Llave de idempotencia opcional (1 a 255 caracteres de `A-Za-z0-9_-`). Repetir la misma operación con la misma llave devuelve la respuesta original sin volver a ejecutarla. Ver la [guía de idempotencia](/docs/idempotency)."
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "example": "https://example.com/landing?utm_source=sms",
                    "description": "URL de destino. Absoluta `http` o `https`, máximo 2048 caracteres. Se rechazan hosts privados o reservados."
                  },
                  "domain": {
                    "type": "string",
                    "example": "go.acme.co",
                    "description": "Dominio donde publicar. Debe estar disponible para tu cuenta (ver `GET /api/v6/urlshortener/domains`). Por defecto, tu dominio predeterminado."
                  },
                  "alias": {
                    "type": "string",
                    "example": "spring-sale",
                    "pattern": "^[a-z0-9_-]{3,32}$",
                    "description": "Código personalizado opcional (3 a 32 caracteres: minúsculas, dígitos, `-` y `_`). Debe estar libre en el dominio y no ser una palabra reservada."
                  },
                  "expiresAt": {
                    "type": "string",
                    "format": "date-time",
                    "example": "2026-12-31T23:59:59Z",
                    "description": "Expiración opcional en ISO-8601, siempre futura. Después de esa fecha el enlace responde `410`."
                  }
                },
                "required": [
                  "url"
                ]
              },
              "example": {
                "url": "https://example.com/landing?utm_source=sms",
                "domain": "go.acme.co",
                "alias": "spring-sale"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Enlace creado. En el caso extremo de que la relectura inmediata falle, `data` trae solo `code` y `domain`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Idempotency-Status": {
                "$ref": "#/components/headers/Idempotency-Status"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/Idempotency-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": 1024,
                    "code": "a1b2c3d",
                    "domain": "go.acme.co",
                    "shortUrl": "https://go.acme.co/a1b2c3d",
                    "longUrl": "https://example.com/landing?utm_source=sms",
                    "alias": false,
                    "active": true,
                    "expiresAt": null,
                    "createdAt": "2026-06-01T15:04:05Z",
                    "clicks": 0,
                    "uniqueClicks": 0,
                    "lastClickAt": null
                  },
                  "meta": {
                    "requestId": "8f0c0e2a4b1d4c8fae2b7a91e0c5d3f6",
                    "timestamp": "2026-06-01T15:04:05+00:00",
                    "responseTimeMs": 7.2
                  }
                }
              }
            }
          },
          "400": {
            "description": "Entrada inválida (URL de destino, alias, expiración o dominio), o llave de idempotencia con formato incorrecto.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "IdempotencyKeyInvalid": {
                    "$ref": "#/components/examples/IdempotencyKeyInvalid"
                  },
                  "UrlInvalid": {
                    "$ref": "#/components/examples/UrlInvalid"
                  },
                  "UrlSchemeNotAllowed": {
                    "$ref": "#/components/examples/UrlSchemeNotAllowed"
                  },
                  "UrlTooLong": {
                    "$ref": "#/components/examples/UrlTooLong"
                  },
                  "UrlBlockedHost": {
                    "$ref": "#/components/examples/UrlBlockedHost"
                  },
                  "AliasInvalid": {
                    "$ref": "#/components/examples/AliasInvalid"
                  },
                  "ExpiresAtInvalid": {
                    "$ref": "#/components/examples/ExpiresAtInvalid"
                  },
                  "DomainNotAllowed": {
                    "$ref": "#/components/examples/DomainNotAllowed"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "403": {
            "description": "La organización está en modo solo lectura, o no puede crear el enlace.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AccountReadOnly": {
                    "$ref": "#/components/examples/AccountReadOnly"
                  }
                }
              }
            }
          },
          "409": {
            "description": "El alias ya está en uso en este dominio, o una petición con la misma llave de idempotencia sigue en proceso.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "IdempotencyInProgress": {
                    "$ref": "#/components/examples/IdempotencyInProgress"
                  },
                  "AliasTaken": {
                    "$ref": "#/components/examples/AliasTaken"
                  },
                  "AliasReserved": {
                    "$ref": "#/components/examples/AliasReserved"
                  }
                }
              }
            }
          },
          "422": {
            "description": "La llave de idempotencia se reusó con un cuerpo distinto.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "IdempotencyKeyReused": {
                    "$ref": "#/components/examples/IdempotencyKeyReused"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "urlshortener.list",
        "tags": [
          "Acortador de URLs"
        ],
        "summary": "Listar enlaces cortos",
        "description": "Lista tus enlaces cortos, del más nuevo al más viejo, con los contadores de clics ya consolidados en cada ítem.\n\n## Alcance y límites\n\n- Acceso: API key con el servicio `urlshortener` habilitado.\n- Límite: 30 solicitudes por minuto.\n- Hasta 100 ítems por página.",
        "x-tps": {
          "limit": 30,
          "period": 60,
          "endpointId": "urlshortener.list"
        },
        "x-scope": {
          "type": "service",
          "value": "urlshortener"
        },
        "x-limit": {
          "requests": 30,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "service",
          "value": "urlshortener"
        },
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Número de página (empieza en 1).",
            "schema": {
              "type": "integer",
              "default": 1,
              "minimum": 1,
              "description": "Número de página (empieza en 1)."
            }
          },
          {
            "name": "perPage",
            "in": "query",
            "required": false,
            "description": "Ítems por página (máximo 100).",
            "schema": {
              "type": "integer",
              "default": 20,
              "minimum": 1,
              "maximum": 100,
              "description": "Ítems por página (máximo 100)."
            }
          },
          {
            "name": "active",
            "in": "query",
            "required": false,
            "description": "Filtra por estado activo.",
            "schema": {
              "type": "boolean",
              "description": "Filtra por estado activo."
            }
          },
          {
            "name": "domain",
            "in": "query",
            "required": false,
            "description": "Filtra por dominio.",
            "schema": {
              "type": "string",
              "description": "Filtra por dominio."
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Coincidencia por subcadena en el código o la URL de destino (mínimo 2 caracteres).",
            "schema": {
              "type": "string",
              "minLength": 2,
              "description": "Coincidencia por subcadena en el código o la URL de destino (mínimo 2 caracteres)."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Página de enlaces.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkListSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "items": [
                      {
                        "id": 1024,
                        "code": "a1b2c3d",
                        "domain": "go.acme.co",
                        "shortUrl": "https://go.acme.co/a1b2c3d",
                        "longUrl": "https://example.com/landing",
                        "active": true,
                        "expiresAt": null,
                        "createdAt": "2026-06-01T15:04:05Z",
                        "clicks": 128,
                        "uniqueClicks": 96,
                        "lastClickAt": "2026-06-01T18:22:10Z"
                      }
                    ],
                    "page": 1,
                    "perPage": 20,
                    "total": 1,
                    "hasMore": false
                  },
                  "meta": {
                    "requestId": "8f0c0e2a4b1d4c8fae2b7a91e0c5d3f6",
                    "timestamp": "2026-06-01T18:30:00+00:00",
                    "responseTimeMs": 5.1
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/urlshortener/domains": {
      "get": {
        "operationId": "urlshortener.domains",
        "tags": [
          "Acortador de URLs"
        ],
        "summary": "Listar dominios disponibles",
        "description": "Lista los dominios donde tu cuenta puede publicar enlaces cortos: los dominios globales de la plataforma más los tuyos, con el predeterminado en primer lugar.\n\n## Alcance y límites\n\n- Acceso: API key con el servicio `urlshortener` habilitado.\n- Límite: 30 solicitudes por minuto.",
        "x-tps": {
          "limit": 30,
          "period": 60,
          "endpointId": "urlshortener.domains"
        },
        "x-scope": {
          "type": "service",
          "value": "urlshortener"
        },
        "x-limit": {
          "requests": 30,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "service",
          "value": "urlshortener"
        },
        "responses": {
          "200": {
            "description": "Dominios disponibles.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainsListSuccess"
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "domain": "go.acme.co",
                      "isDefault": true,
                      "verified": true,
                      "global": false
                    },
                    {
                      "domain": "hbl.li",
                      "isDefault": false,
                      "verified": true,
                      "global": true
                    }
                  ],
                  "meta": {
                    "requestId": "8f0c0e2a4b1d4c8fae2b7a91e0c5d3f6",
                    "timestamp": "2026-06-01T18:30:00+00:00",
                    "responseTimeMs": 2
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/urlshortener/links/{domain}/{code}": {
      "get": {
        "operationId": "urlshortener.get",
        "tags": [
          "Acortador de URLs"
        ],
        "summary": "Obtener un enlace corto",
        "description": "Devuelve un enlace corto que te pertenece. Un enlace de otra cuenta responde `404`: no se revela su existencia.\n\n## Alcance y límites\n\n- Acceso: API key con el servicio `urlshortener` habilitado.\n- Límite: 60 solicitudes por minuto.",
        "x-tps": {
          "limit": 60,
          "period": 60,
          "endpointId": "urlshortener.get"
        },
        "x-scope": {
          "type": "service",
          "value": "urlshortener"
        },
        "x-limit": {
          "requests": 60,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "service",
          "value": "urlshortener"
        },
        "parameters": [
          {
            "name": "domain",
            "in": "path",
            "required": true,
            "description": "Dominio del enlace.",
            "schema": {
              "type": "string",
              "example": "go.acme.co",
              "description": "Dominio del enlace."
            }
          },
          {
            "name": "code",
            "in": "path",
            "required": true,
            "description": "Código corto o alias (en minúsculas).",
            "schema": {
              "type": "string",
              "example": "a1b2c3d",
              "description": "Código corto o alias (en minúsculas)."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "El enlace.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": 1024,
                    "code": "a1b2c3d",
                    "domain": "go.acme.co",
                    "shortUrl": "https://go.acme.co/a1b2c3d",
                    "longUrl": "https://example.com/landing?utm_source=sms",
                    "alias": false,
                    "active": true,
                    "expiresAt": null,
                    "createdAt": "2026-06-01T15:04:05Z",
                    "clicks": 0,
                    "uniqueClicks": 0,
                    "lastClickAt": null
                  },
                  "meta": {
                    "requestId": "8f0c0e2a4b1d4c8fae2b7a91e0c5d3f6",
                    "timestamp": "2026-06-01T15:04:05+00:00",
                    "responseTimeMs": 7.2
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No existe un enlace con ese dominio y código.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "LinkNotFound": {
                    "$ref": "#/components/examples/LinkNotFound"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "urlshortener.update",
        "tags": [
          "Acortador de URLs"
        ],
        "summary": "Actualizar un enlace corto",
        "description": "Actualiza el estado `active`, la fecha `expiresAt` (envía `null` para quitarla) o la `longUrl` de destino, que se revalida. El código o alias es inmutable. Se requiere al menos un campo.\n\n## Alcance y límites\n\n- Acceso: API key con el servicio `urlshortener` habilitado.\n- Límite: 35 solicitudes por segundo.",
        "x-tps": {
          "limit": 35,
          "period": 1,
          "endpointId": "urlshortener.update"
        },
        "x-scope": {
          "type": "service",
          "value": "urlshortener"
        },
        "x-limit": {
          "requests": 35,
          "windowSeconds": 1
        },
        "x-access": {
          "kind": "service",
          "value": "urlshortener"
        },
        "parameters": [
          {
            "name": "domain",
            "in": "path",
            "required": true,
            "description": "Dominio del enlace.",
            "schema": {
              "type": "string",
              "example": "go.acme.co",
              "description": "Dominio del enlace."
            }
          },
          {
            "name": "code",
            "in": "path",
            "required": true,
            "description": "Código corto o alias (en minúsculas).",
            "schema": {
              "type": "string",
              "example": "a1b2c3d",
              "description": "Código corto o alias (en minúsculas)."
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "active": {
                    "type": "boolean",
                    "description": "Activa o desactiva el enlace."
                  },
                  "expiresAt": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Nueva expiración, o `null` para quitarla."
                  },
                  "longUrl": {
                    "type": "string",
                    "format": "uri",
                    "description": "Nueva URL de destino. Se revalida igual que al crear."
                  }
                }
              },
              "example": {
                "active": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Enlace actualizado.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": 1024,
                    "code": "a1b2c3d",
                    "domain": "go.acme.co",
                    "shortUrl": "https://go.acme.co/a1b2c3d",
                    "longUrl": "https://example.com/landing?utm_source=sms",
                    "alias": false,
                    "active": true,
                    "expiresAt": null,
                    "createdAt": "2026-06-01T15:04:05Z",
                    "clicks": 0,
                    "uniqueClicks": 0,
                    "lastClickAt": null
                  },
                  "meta": {
                    "requestId": "8f0c0e2a4b1d4c8fae2b7a91e0c5d3f6",
                    "timestamp": "2026-06-01T15:04:05+00:00",
                    "responseTimeMs": 7.2
                  }
                }
              }
            }
          },
          "400": {
            "description": "No se envió nada para actualizar, o un campo es inválido.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "UrlInvalid": {
                    "$ref": "#/components/examples/UrlInvalid"
                  },
                  "UrlSchemeNotAllowed": {
                    "$ref": "#/components/examples/UrlSchemeNotAllowed"
                  },
                  "UrlTooLong": {
                    "$ref": "#/components/examples/UrlTooLong"
                  },
                  "UrlBlockedHost": {
                    "$ref": "#/components/examples/UrlBlockedHost"
                  },
                  "ExpiresAtInvalid": {
                    "$ref": "#/components/examples/ExpiresAtInvalid"
                  },
                  "NothingToUpdate": {
                    "$ref": "#/components/examples/NothingToUpdate"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No existe un enlace con ese dominio y código.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "LinkNotFound": {
                    "$ref": "#/components/examples/LinkNotFound"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "urlshortener.delete",
        "tags": [
          "Acortador de URLs"
        ],
        "summary": "Eliminar un enlace corto",
        "description": "Borrado **físico**: en la misma transacción se elimina el enlace y toda su analítica (visitas diarias, por hora y por dimensión). No hay forma de recuperarlo; si necesitas conservar los números, expórtalos antes con el endpoint de estadísticas.\n\n## Alcance y límites\n\n- Acceso: API key con el servicio `urlshortener` habilitado.\n- Límite: 35 solicitudes por segundo.",
        "x-tps": {
          "limit": 35,
          "period": 1,
          "endpointId": "urlshortener.delete"
        },
        "x-scope": {
          "type": "service",
          "value": "urlshortener"
        },
        "x-limit": {
          "requests": 35,
          "windowSeconds": 1
        },
        "x-access": {
          "kind": "service",
          "value": "urlshortener"
        },
        "parameters": [
          {
            "name": "domain",
            "in": "path",
            "required": true,
            "description": "Dominio del enlace.",
            "schema": {
              "type": "string",
              "example": "go.acme.co",
              "description": "Dominio del enlace."
            }
          },
          {
            "name": "code",
            "in": "path",
            "required": true,
            "description": "Código corto o alias (en minúsculas).",
            "schema": {
              "type": "string",
              "example": "a1b2c3d",
              "description": "Código corto o alias (en minúsculas)."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Enlace eliminado.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletedSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "deleted": "a1b2c3d",
                    "domain": "go.acme.co"
                  },
                  "meta": {
                    "requestId": "8f0c0e2a4b1d4c8fae2b7a91e0c5d3f6",
                    "timestamp": "2026-06-01T18:30:00+00:00",
                    "responseTimeMs": 3
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No existe un enlace con ese dominio y código.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "LinkNotFound": {
                    "$ref": "#/components/examples/LinkNotFound"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/urlshortener/links/{domain}/{code}/stats": {
      "get": {
        "operationId": "urlshortener.stats",
        "tags": [
          "Acortador de URLs"
        ],
        "summary": "Estadísticas del enlace",
        "description": "Analítica de clics de un enlace: totales, una serie de tiempo y desgloses top-N por dimensión. Los cortes de día y hora se calculan en `America/Bogota`.\n\n## Alcance y límites\n\n- Acceso: API key con el servicio `urlshortener` habilitado.\n- Límite: 120 solicitudes por minuto.\n- La granularidad por hora solo está disponible para rangos de 31 días o menos, dentro de los últimos 90 días.",
        "x-tps": {
          "limit": 120,
          "period": 60,
          "endpointId": "urlshortener.stats"
        },
        "x-scope": {
          "type": "service",
          "value": "urlshortener"
        },
        "x-limit": {
          "requests": 120,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "service",
          "value": "urlshortener"
        },
        "parameters": [
          {
            "name": "domain",
            "in": "path",
            "required": true,
            "description": "Dominio del enlace.",
            "schema": {
              "type": "string",
              "example": "go.acme.co",
              "description": "Dominio del enlace."
            }
          },
          {
            "name": "code",
            "in": "path",
            "required": true,
            "description": "Código corto o alias (en minúsculas).",
            "schema": {
              "type": "string",
              "example": "a1b2c3d",
              "description": "Código corto o alias (en minúsculas)."
            }
          },
          {
            "name": "range",
            "in": "query",
            "required": false,
            "description": "Ventana predefinida. Se ignora si envías `from` o `to`.",
            "schema": {
              "type": "string",
              "enum": [
                "last_24h",
                "last_7d",
                "last_30d",
                "last_90d",
                "this_month",
                "last_month",
                "ytd",
                "all_time"
              ],
              "default": "last_30d",
              "description": "Ventana predefinida. Se ignora si envías `from` o `to`."
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inicio de una ventana personalizada (ISO-8601).",
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Inicio de una ventana personalizada (ISO-8601)."
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Fin de la ventana personalizada (ISO-8601). Por defecto, ahora.",
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Fin de la ventana personalizada (ISO-8601). Por defecto, ahora."
            }
          },
          {
            "name": "granularity",
            "in": "query",
            "required": false,
            "description": "Automática si se omite.",
            "schema": {
              "type": "string",
              "enum": [
                "hour",
                "day"
              ],
              "description": "Automática si se omite."
            }
          },
          {
            "name": "dimensions",
            "in": "query",
            "required": false,
            "description": "Dimensiones de desglose separadas por coma: `country`, `subdivision`, `city`, `device`, `os`, `browser`, `referrer`.",
            "schema": {
              "type": "string",
              "example": "country,device,referrer",
              "description": "Dimensiones de desglose separadas por coma: `country`, `subdivision`, `city`, `device`, `os`, `browser`, `referrer`."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Analítica del enlace.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkStatsSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "code": "a1b2c3d",
                    "domain": "go.acme.co",
                    "range": {
                      "from": "2026-05-03T00:00:00-05:00",
                      "to": "2026-06-01T18:30:00-05:00",
                      "granularity": "day"
                    },
                    "totals": {
                      "clicks": 128,
                      "uniqueClicks": 96,
                      "firstClickAt": "2026-05-04T12:00:00Z",
                      "lastClickAt": "2026-06-01T18:22:10Z"
                    },
                    "series": [
                      {
                        "ts": "2026-05-04",
                        "clicks": 40,
                        "uniqueClicks": 31
                      }
                    ],
                    "breakdowns": {
                      "country": [
                        {
                          "key": "CO",
                          "clicks": 90
                        },
                        {
                          "key": "__other",
                          "clicks": 38
                        }
                      ],
                      "device": [
                        {
                          "key": "mobile",
                          "clicks": 110
                        }
                      ]
                    }
                  },
                  "meta": {
                    "requestId": "8f0c0e2a4b1d4c8fae2b7a91e0c5d3f6",
                    "timestamp": "2026-06-01T18:30:00+00:00",
                    "responseTimeMs": 9.4
                  }
                }
              }
            }
          },
          "400": {
            "description": "Rango o granularidad inválidos.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "StatsRangeInvalid": {
                    "$ref": "#/components/examples/StatsRangeInvalid"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No existe un enlace con ese dominio y código.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "LinkNotFound": {
                    "$ref": "#/components/examples/LinkNotFound"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/tts/synthesize": {
      "post": {
        "operationId": "tts.synthesize",
        "tags": [
          "Text-to-Speech"
        ],
        "summary": "Sintetizar texto a voz",
        "description": "Convierte un texto en un archivo de audio y devuelve su referencia. La síntesis es **síncrona**: la respuesta llega cuando el audio ya está listo.\n\nLa voz se elige con `voice`, usando un `code` del catálogo (ver `GET /api/v6/tts/voices`). El texto admite hasta 4000 caracteres por solicitud.\n\nEl mismo texto con la misma voz se reutiliza automáticamente: si ya generaste ese audio, la respuesta llega con `cached: true` y sin volver a procesarlo.\n\n## Alcance y límites\n\n- Acceso: API key con el servicio `tts` habilitado.\n- Límite: 60 solicitudes por minuto.\n- Hasta 4000 caracteres de texto por solicitud.\n- La vigencia del audio va de 1 hora a 1 año; por defecto son 30 días.\n- Admite `Idempotency-Key`.",
        "x-tps": {
          "limit": 60,
          "period": 60,
          "endpointId": "tts.synthesize"
        },
        "x-scope": {
          "type": "service",
          "value": "tts"
        },
        "x-limit": {
          "requests": 60,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "service",
          "value": "tts"
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "text": {
                    "type": "string",
                    "example": "Hola, te llamamos de Acme para confirmar tu cita.",
                    "minLength": 1,
                    "maxLength": 4000,
                    "description": "Texto a sintetizar. Entre 1 y 4000 caracteres."
                  },
                  "voice": {
                    "type": "string",
                    "example": "es-us-female-2",
                    "description": "Código de voz del catálogo (ver `GET /api/v6/tts/voices`)."
                  },
                  "speakingRate": {
                    "type": "number",
                    "default": 1,
                    "minimum": 0.25,
                    "maximum": 4,
                    "description": "Velocidad de habla, de 0.25 a 4.0. Por defecto 1.0."
                  },
                  "format": {
                    "type": "string",
                    "enum": [
                      "wav"
                    ],
                    "description": "Formato del audio. Por ahora solo `wav`."
                  },
                  "ttlSeconds": {
                    "type": "integer",
                    "default": 2592000,
                    "minimum": 3600,
                    "maximum": 31536000,
                    "description": "Vigencia del audio en segundos (mínimo 1 hora, máximo 1 año). Se fija al crear el audio; si reutilizas uno ya generado, conserva su vigencia original."
                  }
                },
                "required": [
                  "text",
                  "voice"
                ]
              },
              "example": {
                "text": "Hola, te llamamos de Acme para confirmar tu cita.",
                "voice": "es-us-female-2",
                "speakingRate": 1
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Audio listo, recién generado o reutilizado.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Idempotency-Status": {
                "$ref": "#/components/headers/Idempotency-Status"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/Idempotency-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TtsSynthesizeSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "01J9Z4M7K2QABCDEF5GHTV3WXY",
                    "status": "completed",
                    "cached": false,
                    "charCount": 49,
                    "durationMs": 3120,
                    "voice": "es-us-female-2",
                    "cost": "12.500000",
                    "currency": "COP",
                    "audioUrl": "https://storage.hablame.co/tts/9f2c...e1.wav?Expires=...&Signature=..."
                  },
                  "meta": {
                    "requestId": "8f0c0e2a4b1d4c8fae2b7a91e0c5d3f6",
                    "timestamp": "2026-06-23T15:04:05+00:00",
                    "responseTimeMs": 412.5
                  }
                }
              }
            }
          },
          "400": {
            "description": "Entrada inválida: texto vacío o demasiado largo, voz inexistente, o parámetro fuera de rango.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "402": {
            "description": "Saldo insuficiente para generar el audio.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "La API key no tiene habilitado el servicio `tts`, o un tope de gasto impide la generación.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AccountReadOnly": {
                    "$ref": "#/components/examples/AccountReadOnly"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/tts/synthesis/{id}": {
      "get": {
        "operationId": "tts.synthesis.get",
        "tags": [
          "Text-to-Speech"
        ],
        "summary": "Estado de una síntesis",
        "description": "Consulta los metadatos de una solicitud de síntesis por su `id`, el que devuelve `POST /api/v6/tts/synthesize`. Solo accedes a las solicitudes de tu propia organización.\n\n## Alcance y límites\n\n- Acceso: API key con el servicio `tts` habilitado.\n- Límite: 120 solicitudes por minuto.",
        "x-tps": {
          "limit": 120,
          "period": 60,
          "endpointId": "tts.synthesis.get"
        },
        "x-scope": {
          "type": "service",
          "value": "tts"
        },
        "x-limit": {
          "requests": 120,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "service",
          "value": "tts"
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador de la síntesis devuelto al sintetizar.",
            "schema": {
              "type": "string",
              "example": "01J9Z4M7K2QABCDEF5GHTV3WXY",
              "description": "Identificador de la síntesis devuelto al sintetizar."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Metadatos de la síntesis.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TtsSynthesisSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "01J9Z4M7K2QABCDEF5GHTV3WXY",
                    "status": "completed",
                    "cached": false,
                    "charCount": 49,
                    "durationMs": 3120,
                    "language": "es-US",
                    "voice": "es-us-female-2",
                    "tier": "intermediate",
                    "cost": "12.500000",
                    "currency": "COP",
                    "audioUrl": "https://storage.hablame.co/tts/9f2c...e1.wav?Expires=...&Signature=...",
                    "reference": null,
                    "errorCode": null,
                    "createdAt": "2026-06-23 15:04:05"
                  },
                  "meta": {
                    "requestId": "8f0c0e2a4b1d4c8fae2b7a91e0c5d3f6",
                    "timestamp": "2026-06-23T15:06:10+00:00",
                    "responseTimeMs": 2.3
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No existe una síntesis con ese id para tu organización.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/tts/voices": {
      "get": {
        "operationId": "tts.voices.list",
        "tags": [
          "Text-to-Speech"
        ],
        "summary": "Listar voces disponibles",
        "description": "Devuelve el catálogo de voces para síntesis. Cada voz trae un `code` estable que envías en el campo `voice` al sintetizar. El catálogo cambia raras veces: conviene guardarlo en caché de tu lado.\n\n## Alcance y límites\n\n- Acceso: API key con el servicio `tts` habilitado.\n- Límite: 120 solicitudes por minuto.",
        "x-tps": {
          "limit": 120,
          "period": 60,
          "endpointId": "tts.voices.list"
        },
        "x-scope": {
          "type": "service",
          "value": "tts"
        },
        "x-limit": {
          "requests": 120,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "service",
          "value": "tts"
        },
        "parameters": [
          {
            "name": "language",
            "in": "query",
            "required": false,
            "description": "Filtra por idioma y región (BCP-47), por ejemplo `es-US` o `en-US`.",
            "schema": {
              "type": "string",
              "example": "es-US",
              "description": "Filtra por idioma y región (BCP-47), por ejemplo `es-US` o `en-US`."
            }
          },
          {
            "name": "tier",
            "in": "query",
            "required": false,
            "description": "Filtra por nivel de calidad de la voz.",
            "schema": {
              "type": "string",
              "enum": [
                "basic",
                "intermediate",
                "advanced"
              ],
              "description": "Filtra por nivel de calidad de la voz."
            }
          },
          {
            "name": "gender",
            "in": "query",
            "required": false,
            "description": "Filtra por género de la voz.",
            "schema": {
              "type": "string",
              "enum": [
                "female",
                "male"
              ],
              "description": "Filtra por género de la voz."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Catálogo de voces, filtrado si enviaste parámetros.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VoicesListSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "voices": [
                      {
                        "code": "es-us-female-2",
                        "name": "Valentina · Español Latinoamérica",
                        "language": "es-US",
                        "accent": "Español Latinoamérica",
                        "gender": "female",
                        "tier": "intermediate",
                        "sampleRate": 8000
                      },
                      {
                        "code": "en-us-female-1",
                        "name": "Emma · Inglés (EE. UU.)",
                        "language": "en-US",
                        "accent": "Inglés (EE. UU.)",
                        "gender": "female",
                        "tier": "basic",
                        "sampleRate": 8000
                      }
                    ]
                  },
                  "meta": {
                    "requestId": "8f0c0e2a4b1d4c8fae2b7a91e0c5d3f6",
                    "timestamp": "2026-06-23T15:04:05+00:00",
                    "responseTimeMs": 1.4
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "403": {
            "description": "La API key no tiene habilitado el servicio `tts`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/callblasting/calls": {
      "post": {
        "operationId": "callblasting.calls.create",
        "tags": [
          "Call Blasting"
        ],
        "summary": "Llamada con voz generada",
        "description": "Envía una llamada que reproduce un audio generado por texto a voz. El audio se sintetiza al crear el proceso y se reutiliza si ya existía, sin recargo.\n\nLa voz se elige siempre por código del catálogo (`voice`). El audio pregrabado tiene su propio endpoint: `POST /api/v6/callblasting/calls/audio`.\n\n## Alcance y límites\n\n- Acceso: API key con el servicio `callblasting` habilitado.\n- Límite: 60 solicitudes por minuto.\n- Hasta 4000 caracteres de texto por llamada.\n- Hasta 5 intentos por llamada.\n- Admite `Idempotency-Key`.",
        "x-tps": {
          "limit": 60,
          "period": 60,
          "endpointId": "callblasting.calls.create"
        },
        "x-scope": {
          "type": "service",
          "value": "callblasting"
        },
        "x-limit": {
          "requests": 60,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "service",
          "value": "callblasting"
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "string",
                    "example": "573001234567",
                    "description": "Número destino en formato internacional, solo dígitos (7 a 18)."
                  },
                  "text": {
                    "type": "string",
                    "example": "Hola, te llamamos de Acme para confirmar tu cita del jueves.",
                    "minLength": 1,
                    "maxLength": 4000,
                    "description": "Texto a sintetizar (1 a 4000 caracteres)."
                  },
                  "voice": {
                    "type": "string",
                    "example": "es-us-female-1",
                    "description": "Código de voz del catálogo (ver `GET /api/v6/callblasting/voices`)."
                  },
                  "callerId": {
                    "type": "string",
                    "example": "6015551234",
                    "description": "Número que se muestra a quien recibe la llamada."
                  },
                  "clientReference": {
                    "type": "string",
                    "example": "campania-abril-0012",
                    "maxLength": 128,
                    "description": "Tu referencia para correlacionar la llamada (máximo 128 caracteres)."
                  },
                  "repeat": {
                    "type": "integer",
                    "default": 2,
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Veces que se reproduce el mensaje."
                  },
                  "recordingTtlSec": {
                    "type": "integer",
                    "example": 86400,
                    "description": "Retención de la grabación en segundos. Por defecto 24 horas; mínimo 24 horas, máximo 1 año."
                  },
                  "retry": {
                    "type": "object",
                    "properties": {
                      "maxAttempts": {
                        "type": "integer",
                        "default": 2,
                        "minimum": 1,
                        "maximum": 5,
                        "description": "Número de intentos."
                      },
                      "minIntervalSec": {
                        "type": "integer",
                        "default": 300,
                        "minimum": 300,
                        "maximum": 900,
                        "description": "Espera mínima entre intentos, en segundos."
                      },
                      "minSuccessSec": {
                        "type": "integer",
                        "default": 5,
                        "minimum": 1,
                        "maximum": 30,
                        "description": "Duración mínima para contar la llamada como exitosa, en segundos."
                      }
                    },
                    "description": "Ajustes de reintento. Qué estados se reintentan es una política fija del servicio; aquí solo ajustas los límites."
                  }
                },
                "required": [
                  "to",
                  "text",
                  "voice"
                ]
              },
              "example": {
                "to": "573001234567",
                "text": "Hola, te llamamos de Acme para confirmar tu cita del jueves.",
                "voice": "es-us-female-1",
                "clientReference": "campania-abril-0012",
                "repeat": 2
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Llamada encolada.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Idempotency-Status": {
                "$ref": "#/components/headers/Idempotency-Status"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/Idempotency-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallQueuedSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "cb_9f2c0e2a1d8b",
                    "to": "573001234567",
                    "status": "queued",
                    "clientReference": "campania-abril-0012"
                  },
                  "meta": {
                    "requestId": "8f0c0e2a4b1d4c8fae2b7a91e0c5d3f6",
                    "timestamp": "2026-06-23T15:04:05+00:00",
                    "responseTimeMs": 412.5
                  }
                }
              }
            }
          },
          "400": {
            "description": "Número inválido, falta el texto, o la voz no existe.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "402": {
            "description": "Saldo insuficiente para generar el audio de la llamada.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "La API key no tiene habilitado el servicio `callblasting`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AccountReadOnly": {
                    "$ref": "#/components/examples/AccountReadOnly"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/callblasting/calls/audio": {
      "post": {
        "operationId": "callblasting.calls.audio",
        "tags": [
          "Call Blasting"
        ],
        "summary": "Llamada con audio pregrabado",
        "description": "Envía una llamada que reproduce un audio pregrabado. Hay dos formas de hacerlo.\n\nEl resto de los campos es igual que en el envío con texto a voz.\n\n## Alcance y límites\n\n- Acceso: API key con el servicio `callblasting` habilitado.\n- Límite: 60 solicitudes por minuto.\n- El archivo de audio admite hasta 20 MB.\n- Admite `Idempotency-Key`.",
        "x-tps": {
          "limit": 60,
          "period": 60,
          "endpointId": "callblasting.calls.audio"
        },
        "x-scope": {
          "type": "service",
          "value": "callblasting"
        },
        "x-limit": {
          "requests": 60,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "service",
          "value": "callblasting"
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "string",
                    "example": "573001234567",
                    "description": "Número destino en formato internacional, solo dígitos."
                  },
                  "audioId": {
                    "type": "string",
                    "example": "01J9Z4M7K2QABCDEF5GHTV3WXY",
                    "description": "Id de un audio ya subido."
                  },
                  "callerId": {
                    "type": "string",
                    "example": "6015551234",
                    "description": "Número que se muestra a quien recibe la llamada."
                  },
                  "clientReference": {
                    "type": "string",
                    "example": "campania-abril-0012",
                    "maxLength": 128,
                    "description": "Tu referencia para correlacionar la llamada (máximo 128 caracteres)."
                  },
                  "repeat": {
                    "type": "integer",
                    "default": 2,
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Veces que se reproduce el mensaje."
                  },
                  "recordingTtlSec": {
                    "type": "integer",
                    "example": 86400,
                    "description": "Retención de la grabación en segundos. Por defecto 24 horas; mínimo 24 horas, máximo 1 año."
                  },
                  "retry": {
                    "type": "object",
                    "properties": {
                      "maxAttempts": {
                        "type": "integer",
                        "default": 2,
                        "minimum": 1,
                        "maximum": 5,
                        "description": "Número de intentos."
                      },
                      "minIntervalSec": {
                        "type": "integer",
                        "default": 300,
                        "minimum": 300,
                        "maximum": 900,
                        "description": "Espera mínima entre intentos, en segundos."
                      },
                      "minSuccessSec": {
                        "type": "integer",
                        "default": 5,
                        "minimum": 1,
                        "maximum": 30,
                        "description": "Duración mínima para contar la llamada como exitosa, en segundos."
                      }
                    },
                    "description": "Ajustes de reintento. Qué estados se reintentan es una política fija del servicio; aquí solo ajustas los límites."
                  }
                },
                "required": [
                  "to",
                  "audioId"
                ]
              },
              "example": {
                "to": "573001234567",
                "audioId": "01J9Z4M7K2QABCDEF5GHTV3WXY",
                "clientReference": "campania-abril-0012"
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "string",
                    "example": "573001234567",
                    "description": "Número destino en formato internacional."
                  },
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "Archivo de audio (wav, mp3, ogg o m4a; máximo 20 MB)."
                  },
                  "callerId": {
                    "type": "string",
                    "description": "Número que se muestra a quien recibe la llamada."
                  },
                  "clientReference": {
                    "type": "string",
                    "description": "Tu referencia para correlacionar la llamada."
                  },
                  "repeat": {
                    "type": "integer",
                    "description": "Veces que se reproduce el mensaje."
                  },
                  "recordingTtlSec": {
                    "type": "integer",
                    "description": "Retención de la grabación en segundos."
                  }
                },
                "required": [
                  "to",
                  "file"
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Llamada encolada.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Idempotency-Status": {
                "$ref": "#/components/headers/Idempotency-Status"
              },
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/Idempotency-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AudioCallQueuedSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "cb_9f2c0e2a1d8b",
                    "to": "573001234567",
                    "status": "queued",
                    "audioId": "01J9Z4M7K2QABCDEF5GHTV3WXY",
                    "clientReference": "campania-abril-0012"
                  },
                  "meta": {
                    "requestId": "8f0c0e2a4b1d4c8fae2b7a91e0c5d3f6",
                    "timestamp": "2026-06-23T15:04:05+00:00",
                    "responseTimeMs": 22.7
                  }
                }
              }
            }
          },
          "400": {
            "description": "Número inválido, falta el audio (`file` o `audioId`), formato no soportado o audio inválido.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "403": {
            "description": "La API key no tiene habilitado el servicio `callblasting`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AccountReadOnly": {
                    "$ref": "#/components/examples/AccountReadOnly"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No existe un audio con ese `audioId` para tu organización.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "El audio está vacío o supera el tamaño máximo permitido.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/callblasting/calls/{id}": {
      "get": {
        "operationId": "callblasting.calls.get",
        "tags": [
          "Call Blasting"
        ],
        "summary": "Estado de una llamada",
        "description": "Devuelve el estado de una llamada y el histórico de sus intentos: si contestaron, duración, causa de colgado, grabación y dígitos marcados. Solo accedes a llamadas de tu organización.\n\n## Alcance y límites\n\n- Acceso: API key con el servicio `callblasting` habilitado.\n- Límite: 120 solicitudes por minuto.",
        "x-tps": {
          "limit": 120,
          "period": 60,
          "endpointId": "callblasting.calls.get"
        },
        "x-scope": {
          "type": "service",
          "value": "callblasting"
        },
        "x-limit": {
          "requests": 120,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "service",
          "value": "callblasting"
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id de la llamada devuelto al crearla.",
            "schema": {
              "type": "string",
              "example": "cb_9f2c0e2a1d8b",
              "description": "Id de la llamada devuelto al crearla."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Estado de la llamada y sus intentos.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "cb_9f2c0e2a1d8b",
                    "to": "573001234567",
                    "clientReference": "campania-abril-0012",
                    "status": "success",
                    "attemptsCount": 1,
                    "attempts": [
                      {
                        "attempt": 1,
                        "status": "completed",
                        "answered": true,
                        "audioPlayed": true,
                        "durationTotal": 18,
                        "durationAir": 12,
                        "hangupCause": 16,
                        "causeText": "Normal Clearing",
                        "recorded": false,
                        "dtmf": null,
                        "startedAt": "2026-06-23 15:04:06",
                        "answeredAt": "2026-06-23 15:04:09",
                        "endedAt": "2026-06-23 15:04:21"
                      }
                    ],
                    "createdAt": "2026-06-23 15:04:05"
                  },
                  "meta": {
                    "requestId": "8f0c0e2a4b1d4c8fae2b7a91e0c5d3f6",
                    "timestamp": "2026-06-23T15:05:00+00:00",
                    "responseTimeMs": 3.1
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No existe una llamada con ese id para tu organización.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/callblasting/audios": {
      "post": {
        "operationId": "callblasting.audios.upload",
        "tags": [
          "Call Blasting"
        ],
        "summary": "Subir un audio pregrabado",
        "description": "Sube un audio pregrabado que queda en tu catálogo para reutilizarlo en cuantas llamadas quieras. Acepta `wav`, `mp3`, `ogg` y `m4a`, hasta 20 MB, y lo dejamos listo para la red de voz.\n\nDevuelve un `id` que luego envías en `audioId` al crear una llamada. Subir dos veces el mismo audio devuelve el mismo `id`: no se duplica.\n\n## Alcance y límites\n\n- Acceso: API key con el servicio `callblasting` habilitado.\n- Límite: 30 solicitudes por minuto.\n- Formatos aceptados: wav, mp3, ogg y m4a. Hasta 20 MB.",
        "x-tps": {
          "limit": 30,
          "period": 60,
          "endpointId": "callblasting.audios.upload"
        },
        "x-scope": {
          "type": "service",
          "value": "callblasting"
        },
        "x-limit": {
          "requests": 30,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "service",
          "value": "callblasting"
        },
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "Archivo de audio (wav, mp3, ogg o m4a; máximo 20 MB)."
                  },
                  "name": {
                    "type": "string",
                    "example": "Bienvenida campaña abril",
                    "maxLength": 120,
                    "description": "Etiqueta opcional para identificar el audio (máximo 120 caracteres)."
                  }
                },
                "required": [
                  "file"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Audio subido, o reutilizado si ya existía.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AudioUploadSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "01J9Z4M7K2QABCDEF5GHTV3WXY",
                    "durationMs": 4200,
                    "format": "mp3",
                    "sizeBytes": 67200,
                    "cached": false
                  },
                  "meta": {
                    "requestId": "8f0c0e2a4b1d4c8fae2b7a91e0c5d3f6",
                    "timestamp": "2026-06-23T15:04:05+00:00",
                    "responseTimeMs": 180.4
                  }
                }
              }
            }
          },
          "400": {
            "description": "Falta el archivo, el formato no está soportado, o el audio no se pudo procesar.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "403": {
            "description": "La API key no tiene habilitado el servicio `callblasting`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AccountReadOnly": {
                    "$ref": "#/components/examples/AccountReadOnly"
                  }
                }
              }
            }
          },
          "413": {
            "description": "El audio está vacío o supera el tamaño máximo permitido.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "callblasting.audios.list",
        "tags": [
          "Call Blasting"
        ],
        "summary": "Listar audios pregrabados",
        "description": "Devuelve los audios pregrabados de tu organización, con su `id` (el que envías en `audioId`), nombre, formato de origen, duración y tamaño.\n\n## Alcance y límites\n\n- Acceso: API key con el servicio `callblasting` habilitado.\n- Límite: 60 solicitudes por minuto.",
        "x-tps": {
          "limit": 60,
          "period": 60,
          "endpointId": "callblasting.audios.list"
        },
        "x-scope": {
          "type": "service",
          "value": "callblasting"
        },
        "x-limit": {
          "requests": 60,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "service",
          "value": "callblasting"
        },
        "responses": {
          "200": {
            "description": "Catálogo de audios.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AudiosListSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "audios": [
                      {
                        "id": "01J9Z4M7K2QABCDEF5GHTV3WXY",
                        "name": "Bienvenida campaña abril",
                        "originalName": "bienvenida.mp3",
                        "format": "mp3",
                        "durationMs": 4200,
                        "sizeBytes": 67200,
                        "createdAt": "2026-06-23 15:04:05"
                      }
                    ]
                  },
                  "meta": {
                    "requestId": "8f0c0e2a4b1d4c8fae2b7a91e0c5d3f6",
                    "timestamp": "2026-06-23T15:04:05+00:00",
                    "responseTimeMs": 1.9
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "403": {
            "description": "La API key no tiene habilitado el servicio `callblasting`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/callblasting/voices": {
      "get": {
        "operationId": "callblasting.voices.list",
        "tags": [
          "Call Blasting"
        ],
        "summary": "Listar voces disponibles",
        "description": "Devuelve el catálogo de voces disponibles para la síntesis de texto a voz de Call Blasting. Cada voz trae un `code` estable que luego envías en el campo `voice` al crear una llamada con `text`.\n\nEl catálogo cambia raras veces: conviene guardarlo en caché de tu lado.\n\n## Alcance y límites\n\n- Acceso: API key con el servicio `callblasting` habilitado.\n- Límite: 120 solicitudes por minuto.",
        "x-tps": {
          "limit": 120,
          "period": 60,
          "endpointId": "callblasting.voices.list"
        },
        "x-scope": {
          "type": "service",
          "value": "callblasting"
        },
        "x-limit": {
          "requests": 120,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "service",
          "value": "callblasting"
        },
        "parameters": [
          {
            "name": "language",
            "in": "query",
            "required": false,
            "description": "Filtra por idioma y región (BCP-47), por ejemplo `es-US` o `en-US`.",
            "schema": {
              "type": "string",
              "example": "es-US",
              "description": "Filtra por idioma y región (BCP-47), por ejemplo `es-US` o `en-US`."
            }
          },
          {
            "name": "tier",
            "in": "query",
            "required": false,
            "description": "Filtra por nivel de calidad de la voz.",
            "schema": {
              "type": "string",
              "enum": [
                "basic",
                "intermediate",
                "advanced"
              ],
              "description": "Filtra por nivel de calidad de la voz."
            }
          },
          {
            "name": "gender",
            "in": "query",
            "required": false,
            "description": "Filtra por género de la voz.",
            "schema": {
              "type": "string",
              "enum": [
                "female",
                "male"
              ],
              "description": "Filtra por género de la voz."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Catálogo de voces, filtrado si enviaste parámetros.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VoicesListSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "voices": [
                      {
                        "code": "es-us-female-2",
                        "name": "Valentina · Español Latinoamérica",
                        "language": "es-US",
                        "accent": "Español Latinoamérica",
                        "gender": "female",
                        "tier": "intermediate",
                        "sampleRate": 8000
                      },
                      {
                        "code": "es-us-male-7",
                        "name": "Nicolás · Español Latinoamérica",
                        "language": "es-US",
                        "accent": "Español Latinoamérica",
                        "gender": "male",
                        "tier": "advanced",
                        "sampleRate": 8000
                      }
                    ]
                  },
                  "meta": {
                    "requestId": "8f0c0e2a4b1d4c8fae2b7a91e0c5d3f6",
                    "timestamp": "2026-06-23T15:04:05+00:00",
                    "responseTimeMs": 1.4
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "403": {
            "description": "La API key no tiene habilitado el servicio `callblasting`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/account/billing": {
      "get": {
        "operationId": "account.billing",
        "tags": [
          "Cuenta"
        ],
        "summary": "Resumen de facturación",
        "description": "Devuelve el núcleo de la facturación de la organización dueña de la API key: modalidad (prepago o postpago), moneda, saldo disponible en prepago o cupo restante en postpago. El detalle —topes, pagos y ajustes— vive en `/api/v6/account/limits`, `/api/v6/account/payments` y `/api/v6/account/adjustments`.\n\nEl campo `available` se calcula igual que el motor de gasto en vivo: en prepago `balance − consumed`; en postpago `creditLimit − consumed`. Todos los montos son cadenas decimales con 6 decimales, en la `currency` de la cuenta.\n\n## Alcance y límites\n\n- Acceso: API key con la capacidad `account` concedida.\n- Límite: 20 solicitudes por minuto.",
        "x-tps": {
          "limit": 20,
          "period": 60,
          "endpointId": "account.billing"
        },
        "x-scope": {
          "type": "capability",
          "value": "account"
        },
        "x-limit": {
          "requests": 20,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "capability",
          "value": "account"
        },
        "responses": {
          "200": {
            "description": "Resumen de facturación.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingSummarySuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "account": {
                      "id": 10000003,
                      "displayName": "Acme S.A.S.",
                      "currency": "COP",
                      "status": "active"
                    },
                    "billing": {
                      "mode": "prepaid",
                      "currency": "COP",
                      "period": "2026-06",
                      "available": "108500.000000",
                      "balance": "108500.000000",
                      "consumed": "0.000000",
                      "reserve": "0.000000",
                      "creditLimit": "0.000000"
                    }
                  },
                  "meta": {
                    "requestId": "b9b1704baffab21150213c02fd853975",
                    "timestamp": "2026-06-02T19:43:59+00:00",
                    "responseTimeMs": 6.1
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "403": {
            "description": "La API key no tiene la capacidad requerida, o la organización no puede ejecutar la solicitud.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AccountNotActive": {
                    "$ref": "#/components/examples/AccountNotActive"
                  },
                  "AccountBlocked": {
                    "$ref": "#/components/examples/AccountBlocked"
                  },
                  "AccountConfigNotFound": {
                    "$ref": "#/components/examples/AccountConfigNotFound"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes. Espera lo que indique `Retry-After`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/account/usage": {
      "get": {
        "operationId": "account.usage",
        "tags": [
          "Cuenta"
        ],
        "summary": "Consumo por scope",
        "description": "Consumo de la cuenta desglosado por scope (organización, centro de costo, usuario, API key o servicio) para un periodo. El campo `total` es el consumo de `org:0`, es decir el gran total. Los montos vienen con 6 decimales.\n\n## Alcance y límites\n\n- Acceso: API key con la capacidad `account` concedida.\n- Límite: 20 solicitudes por minuto.",
        "x-tps": {
          "limit": 20,
          "period": 60,
          "endpointId": "account.usage"
        },
        "x-scope": {
          "type": "capability",
          "value": "account"
        },
        "x-limit": {
          "requests": 20,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "capability",
          "value": "account"
        },
        "parameters": [
          {
            "name": "period",
            "in": "query",
            "required": false,
            "description": "Periodo en formato `YYYY-MM`. Por defecto, el mes en curso.",
            "schema": {
              "type": "string",
              "example": "2026-06",
              "description": "Periodo en formato `YYYY-MM`. Por defecto, el mes en curso."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Consumo del periodo.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "period": "2026-06",
                    "total": "108.000000",
                    "scopes": [
                      {
                        "scopeType": "org",
                        "scopeRef": "0",
                        "consumed": "108.000000"
                      },
                      {
                        "scopeType": "cost_center",
                        "scopeRef": "17",
                        "consumed": "108.000000"
                      },
                      {
                        "scopeType": "service",
                        "scopeRef": "urlshortener",
                        "consumed": "108.000000"
                      }
                    ]
                  },
                  "meta": {
                    "requestId": "b9b1704baffab21150213c02fd853975",
                    "timestamp": "2026-06-02T19:43:59+00:00",
                    "responseTimeMs": 4.1
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "403": {
            "description": "La API key no tiene la capacidad requerida, o la organización no puede ejecutar la solicitud.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AccountNotActive": {
                    "$ref": "#/components/examples/AccountNotActive"
                  },
                  "AccountBlocked": {
                    "$ref": "#/components/examples/AccountBlocked"
                  },
                  "AccountConfigNotFound": {
                    "$ref": "#/components/examples/AccountConfigNotFound"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes. Espera lo que indique `Retry-After`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/account/payments": {
      "get": {
        "operationId": "account.payments",
        "tags": [
          "Cuenta"
        ],
        "summary": "Historial de pagos",
        "description": "Historial de pagos: recargas de saldo prepago y pagos de factura postpago vienen de la misma fuente. El campo `kind` distingue entre `recharge` e `invoice_payment`. Los montos vienen con 6 decimales.\n\nPaginación con `limit` (por defecto 20, máximo 100) y `offset` (por defecto 0). La respuesta trae `data.items` y `data.pagination`.\n\n## Alcance y límites\n\n- Acceso: API key con la capacidad `account` concedida.\n- Límite: 30 solicitudes por minuto.\n- Paginación con `limit` (por defecto 20, máximo 100) y `offset` (por defecto 0). La respuesta trae `data.items` y `data.pagination`.",
        "x-tps": {
          "limit": 30,
          "period": 60,
          "endpointId": "account.payments"
        },
        "x-scope": {
          "type": "capability",
          "value": "account"
        },
        "x-limit": {
          "requests": 30,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "capability",
          "value": "account"
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Cuántos ítems traer (1 a 100).",
            "schema": {
              "type": "integer",
              "default": 20,
              "minimum": 1,
              "maximum": 100,
              "description": "Cuántos ítems traer (1 a 100)."
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Desde qué posición empezar.",
            "schema": {
              "type": "integer",
              "default": 0,
              "description": "Desde qué posición empezar."
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filtro opcional por estado.",
            "schema": {
              "type": "string",
              "description": "Filtro opcional por estado."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Página de pagos.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentsListSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "items": [
                      {
                        "reference": "HBL-3WWG4VEH92",
                        "kind": "recharge",
                        "amount": "50000.000000",
                        "currency": "COP",
                        "status": "approved",
                        "method": "new_card",
                        "processor": "epayco",
                        "createdAt": "2026-05-18 12:30:00",
                        "approvedAt": "2026-05-18 12:31:15",
                        "expiresAt": null
                      }
                    ],
                    "pagination": {
                      "limit": 20,
                      "offset": 0,
                      "returned": 1,
                      "hasMore": false
                    }
                  },
                  "meta": {
                    "requestId": "b9b1704baffab21150213c02fd853975",
                    "timestamp": "2026-06-02T19:43:59+00:00",
                    "responseTimeMs": 4.1
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "403": {
            "description": "La API key no tiene la capacidad requerida, o la organización no puede ejecutar la solicitud.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AccountNotActive": {
                    "$ref": "#/components/examples/AccountNotActive"
                  },
                  "AccountBlocked": {
                    "$ref": "#/components/examples/AccountBlocked"
                  },
                  "AccountConfigNotFound": {
                    "$ref": "#/components/examples/AccountConfigNotFound"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes. Espera lo que indique `Retry-After`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/account/adjustments": {
      "get": {
        "operationId": "account.adjustments",
        "tags": [
          "Cuenta"
        ],
        "summary": "Ajustes de saldo",
        "description": "Ajustes manuales de saldo de la cuenta. Los gestiona Hablame con doble aprobación (quien lo registra no es quien lo aprueba). Los montos vienen con 6 decimales.\n\nPaginación con `limit` (por defecto 20, máximo 100) y `offset` (por defecto 0). La respuesta trae `data.items` y `data.pagination`.\n\n## Alcance y límites\n\n- Acceso: API key con la capacidad `account` concedida.\n- Límite: 30 solicitudes por minuto.\n- Paginación con `limit` (por defecto 20, máximo 100) y `offset` (por defecto 0). La respuesta trae `data.items` y `data.pagination`.",
        "x-tps": {
          "limit": 30,
          "period": 60,
          "endpointId": "account.adjustments"
        },
        "x-scope": {
          "type": "capability",
          "value": "account"
        },
        "x-limit": {
          "requests": 30,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "capability",
          "value": "account"
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Cuántos ítems traer (1 a 100).",
            "schema": {
              "type": "integer",
              "default": 20,
              "minimum": 1,
              "maximum": 100,
              "description": "Cuántos ítems traer (1 a 100)."
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Desde qué posición empezar.",
            "schema": {
              "type": "integer",
              "default": 0,
              "description": "Desde qué posición empezar."
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filtro opcional por estado.",
            "schema": {
              "type": "string",
              "description": "Filtro opcional por estado."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Página de ajustes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdjustmentsListSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "items": [
                      {
                        "reference": "ADJ-20260602-6f0cf241",
                        "direction": "debit",
                        "amount": "10000.000000",
                        "currency": "COP",
                        "category": "manual",
                        "status": "approved",
                        "effectiveDate": "2026-06-02",
                        "reason": "ajuste inicial",
                        "createdAt": "2026-06-02 10:00:00"
                      }
                    ],
                    "pagination": {
                      "limit": 20,
                      "offset": 0,
                      "returned": 1,
                      "hasMore": false
                    }
                  },
                  "meta": {
                    "requestId": "b9b1704baffab21150213c02fd853975",
                    "timestamp": "2026-06-02T19:43:59+00:00",
                    "responseTimeMs": 4.1
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "403": {
            "description": "La API key no tiene la capacidad requerida, o la organización no puede ejecutar la solicitud.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AccountNotActive": {
                    "$ref": "#/components/examples/AccountNotActive"
                  },
                  "AccountBlocked": {
                    "$ref": "#/components/examples/AccountBlocked"
                  },
                  "AccountConfigNotFound": {
                    "$ref": "#/components/examples/AccountConfigNotFound"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes. Espera lo que indique `Retry-After`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/account/limits": {
      "get": {
        "operationId": "account.limits",
        "tags": [
          "Cuenta"
        ],
        "summary": "Topes de gasto",
        "description": "Topes de gasto activos de la cuenta, por scope. Es un endpoint de solo lectura: los topes se configuran desde el portal de clientes. Los montos vienen con 6 decimales y `thresholds` son los porcentajes en los que se dispara una alerta.\n\n## Alcance y límites\n\n- Acceso: API key con la capacidad `account` concedida.\n- Límite: 30 solicitudes por minuto.",
        "x-tps": {
          "limit": 30,
          "period": 60,
          "endpointId": "account.limits"
        },
        "x-scope": {
          "type": "capability",
          "value": "account"
        },
        "x-limit": {
          "requests": 30,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "capability",
          "value": "account"
        },
        "responses": {
          "200": {
            "description": "Topes activos.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LimitsListSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "items": [
                      {
                        "scopeType": "org",
                        "scopeRef": "0",
                        "amount": "1000000.000000",
                        "period": "monthly",
                        "thresholds": [
                          80,
                          95
                        ]
                      }
                    ]
                  },
                  "meta": {
                    "requestId": "b9b1704baffab21150213c02fd853975",
                    "timestamp": "2026-06-02T19:43:59+00:00",
                    "responseTimeMs": 4.1
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "403": {
            "description": "La API key no tiene la capacidad requerida, o la organización no puede ejecutar la solicitud.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AccountNotActive": {
                    "$ref": "#/components/examples/AccountNotActive"
                  },
                  "AccountBlocked": {
                    "$ref": "#/components/examples/AccountBlocked"
                  },
                  "AccountConfigNotFound": {
                    "$ref": "#/components/examples/AccountConfigNotFound"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes. Espera lo que indique `Retry-After`.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/account/organization": {
      "get": {
        "operationId": "account.organization",
        "tags": [
          "Organización"
        ],
        "summary": "Organización",
        "description": "Perfil de la organización dueña de la API key: identidad y configuración. No incluye saldo ni cupo; eso vive en [Resumen de facturación](/docs/reference/account/billing).\n\n## Alcance y límites\n\n- Acceso: API key con la capacidad `directory` concedida.\n- Límite: 60 solicitudes por minuto.",
        "x-tps": {
          "limit": 60,
          "period": 60,
          "endpointId": "account.organization"
        },
        "x-scope": {
          "type": "capability",
          "value": "directory"
        },
        "x-limit": {
          "requests": 60,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "capability",
          "value": "directory"
        },
        "responses": {
          "200": {
            "description": "Perfil de la organización.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": 10000003,
                    "displayName": "Acme S.A.S.",
                    "currency": "COP",
                    "billingMode": "prepaid",
                    "status": "active",
                    "blocks": {
                      "general": false,
                      "collections": false,
                      "security": false
                    },
                    "onboardedAt": "2026-05-14 11:31:56"
                  },
                  "meta": {
                    "requestId": "b9b1704baffab21150213c02fd853975",
                    "timestamp": "2026-06-02T19:43:59+00:00",
                    "responseTimeMs": 4.1
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "403": {
            "description": "La API key no tiene la capacidad requerida, o la organización no puede ejecutar la solicitud.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AccountNotActive": {
                    "$ref": "#/components/examples/AccountNotActive"
                  },
                  "AccountBlocked": {
                    "$ref": "#/components/examples/AccountBlocked"
                  },
                  "AccountConfigNotFound": {
                    "$ref": "#/components/examples/AccountConfigNotFound"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/account/cost-centers": {
      "get": {
        "operationId": "account.costCenters",
        "tags": [
          "Organización"
        ],
        "summary": "Centros de costo",
        "description": "Centros de costo de la organización. Cada API key está vinculada a uno, y de ahí sale la atribución del consumo.\n\n## Alcance y límites\n\n- Acceso: API key con la capacidad `directory` concedida.\n- Límite: 60 solicitudes por minuto.",
        "x-tps": {
          "limit": 60,
          "period": 60,
          "endpointId": "account.costCenters"
        },
        "x-scope": {
          "type": "capability",
          "value": "directory"
        },
        "x-limit": {
          "requests": 60,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "capability",
          "value": "directory"
        },
        "responses": {
          "200": {
            "description": "Centros de costo.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CostCentersListSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "items": [
                      {
                        "id": 17,
                        "code": "1607230448",
                        "name": "Centro de costos general",
                        "status": "active",
                        "isDefault": true
                      }
                    ]
                  },
                  "meta": {
                    "requestId": "b9b1704baffab21150213c02fd853975",
                    "timestamp": "2026-06-02T19:43:59+00:00",
                    "responseTimeMs": 4.1
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "403": {
            "description": "La API key no tiene la capacidad requerida, o la organización no puede ejecutar la solicitud.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AccountNotActive": {
                    "$ref": "#/components/examples/AccountNotActive"
                  },
                  "AccountBlocked": {
                    "$ref": "#/components/examples/AccountBlocked"
                  },
                  "AccountConfigNotFound": {
                    "$ref": "#/components/examples/AccountConfigNotFound"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/account/users": {
      "get": {
        "operationId": "account.users",
        "tags": [
          "Organización"
        ],
        "summary": "Usuarios",
        "description": "Usuarios (miembros) de la organización, con su rol, centro de costo y estado. Contiene datos personales: trátalos como tales en tus propios sistemas.\n\nPaginación con `limit` (por defecto 20, máximo 100) y `offset` (por defecto 0). La respuesta trae `data.items` y `data.pagination`.\n\n## Alcance y límites\n\n- Acceso: API key con la capacidad `directory` concedida.\n- Límite: 30 solicitudes por minuto.\n- Paginación con `limit` (por defecto 20, máximo 100) y `offset` (por defecto 0). La respuesta trae `data.items` y `data.pagination`.",
        "x-tps": {
          "limit": 30,
          "period": 60,
          "endpointId": "account.users"
        },
        "x-scope": {
          "type": "capability",
          "value": "directory"
        },
        "x-limit": {
          "requests": 30,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "capability",
          "value": "directory"
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Cuántos ítems traer (1 a 100).",
            "schema": {
              "type": "integer",
              "default": 20,
              "minimum": 1,
              "maximum": 100,
              "description": "Cuántos ítems traer (1 a 100)."
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Desde qué posición empezar.",
            "schema": {
              "type": "integer",
              "default": 0,
              "description": "Desde qué posición empezar."
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filtro opcional por estado.",
            "schema": {
              "type": "string",
              "description": "Filtro opcional por estado."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Página de usuarios.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsersListSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "items": [
                      {
                        "userId": 10000,
                        "firstName": "Diego",
                        "lastName": "Pérez",
                        "email": "dev@acme.co",
                        "roles": [
                          "owner"
                        ],
                        "costCenterId": 17,
                        "status": "active",
                        "joinedAt": "2026-05-14 11:31:56",
                        "lastUsedAt": "2026-06-02 09:00:00"
                      }
                    ],
                    "pagination": {
                      "limit": 20,
                      "offset": 0,
                      "returned": 1,
                      "hasMore": false
                    }
                  },
                  "meta": {
                    "requestId": "b9b1704baffab21150213c02fd853975",
                    "timestamp": "2026-06-02T19:43:59+00:00",
                    "responseTimeMs": 4.1
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  },
                  "AuthServiceNotAllowed": {
                    "$ref": "#/components/examples/AuthServiceNotAllowed"
                  }
                }
              }
            }
          },
          "403": {
            "description": "La API key no tiene la capacidad requerida, o la organización no puede ejecutar la solicitud.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AccountNotActive": {
                    "$ref": "#/components/examples/AccountNotActive"
                  },
                  "AccountBlocked": {
                    "$ref": "#/components/examples/AccountBlocked"
                  },
                  "AccountConfigNotFound": {
                    "$ref": "#/components/examples/AccountConfigNotFound"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/status": {
      "get": {
        "operationId": "platform.status",
        "tags": [
          "Plataforma"
        ],
        "summary": "Estado de servicios",
        "description": "Estado operativo de los servicios de Hablame en forma compacta, pensado para integrarlo en tu propio monitoreo. Cuando no hay dato disponible para un componente, su `status` llega como `unknown`.\n\n## Alcance y límites\n\n- Acceso: Cualquier API key válida.\n- Límite: 60 solicitudes por minuto.",
        "x-tps": {
          "limit": 60,
          "period": 60,
          "endpointId": "platform.status"
        },
        "x-scope": {
          "type": "universal"
        },
        "x-limit": {
          "requests": 60,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "universal"
        },
        "responses": {
          "200": {
            "description": "Estado de los servicios.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlatformStatusSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "status": "operational",
                    "updatedAt": "2026-06-02T19:40:00+00:00",
                    "stale": false,
                    "components": [
                      {
                        "name": "API",
                        "status": "operational"
                      }
                    ],
                    "incidents": [],
                    "maintenance": []
                  },
                  "meta": {
                    "requestId": "b9b1704baffab21150213c02fd853975",
                    "timestamp": "2026-06-02T19:43:59+00:00",
                    "responseTimeMs": 4.1
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  }
                }
              }
            }
          },
          "403": {
            "description": "La organización no puede ejecutar la solicitud.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AccountNotActive": {
                    "$ref": "#/components/examples/AccountNotActive"
                  },
                  "AccountBlocked": {
                    "$ref": "#/components/examples/AccountBlocked"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v6/catalog/services": {
      "get": {
        "operationId": "catalog.services",
        "tags": [
          "Plataforma"
        ],
        "summary": "Catálogo de servicios",
        "description": "Catálogo de los servicios vivos de la plataforma, con el indicador `enabled` que dice si esta cuenta tiene acceso a cada uno. Útil para construir menús o validaciones en tu propia interfaz sin cablear la lista a mano.\n\n## Alcance y límites\n\n- Acceso: Cualquier API key válida.\n- Límite: 60 solicitudes por minuto.",
        "x-tps": {
          "limit": 60,
          "period": 60,
          "endpointId": "catalog.services"
        },
        "x-scope": {
          "type": "universal"
        },
        "x-limit": {
          "requests": 60,
          "windowSeconds": 60
        },
        "x-access": {
          "kind": "universal"
        },
        "responses": {
          "200": {
            "description": "Catálogo de servicios.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServicesListSuccess"
                },
                "example": {
                  "success": true,
                  "data": {
                    "items": [
                      {
                        "code": "sms",
                        "name": "SMS",
                        "description": "Mensajería de texto",
                        "icon": "chat",
                        "enabled": true
                      }
                    ]
                  },
                  "meta": {
                    "requestId": "b9b1704baffab21150213c02fd853975",
                    "timestamp": "2026-06-02T19:43:59+00:00",
                    "responseTimeMs": 4.1
                  }
                }
              }
            }
          },
          "401": {
            "description": "Credenciales inválidas o faltantes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AuthRequired": {
                    "$ref": "#/components/examples/AuthRequired"
                  },
                  "AuthInvalidKey": {
                    "$ref": "#/components/examples/AuthInvalidKey"
                  }
                }
              }
            }
          },
          "403": {
            "description": "La organización no puede ejecutar la solicitud.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "AccountNotActive": {
                    "$ref": "#/components/examples/AccountNotActive"
                  },
                  "AccountBlocked": {
                    "$ref": "#/components/examples/AccountBlocked"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Se excedió el límite de solicitudes.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "RateTpsExceeded": {
                    "$ref": "#/components/examples/RateTpsExceeded"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Envía tu API key en el header `Authorization`:\n\n```\nAuthorization: Bearer TU_API_KEY\n```\n\nLas API keys solo se muestran al crearlas. Guárdalas en un gestor de secretos."
      }
    },
    "schemas": {
      "Envelope": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "meta": {
            "$ref": "#/components/schemas/Meta"
          }
        },
        "required": [
          "success",
          "meta"
        ],
        "description": "Parte común a toda respuesta: `success` y `meta` van siempre, en éxito y en error."
      },
      "Meta": {
        "type": "object",
        "properties": {
          "requestId": {
            "type": "string",
            "example": "b9b1704baffab21150213c02fd853975",
            "description": "Identificador único de la solicitud. Cítalo al contactar soporte."
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "example": "2026-06-04T19:43:59+00:00",
            "description": "Fecha y hora en que se construyó la respuesta (ISO 8601 con offset)."
          },
          "responseTimeMs": {
            "type": "number",
            "example": 4.24,
            "description": "Tiempo (ms) que tomó procesar la solicitud en el servidor."
          },
          "warnings": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              },
              "required": [
                "code",
                "message"
              ]
            },
            "description": "Advertencias no fatales, indexadas por clave. Solo presente cuando hay alguna."
          }
        },
        "required": [
          "requestId",
          "timestamp",
          "responseTimeMs"
        ],
        "description": "Metadatos de la respuesta."
      },
      "Error": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "example": "AUTH_REQUIRED",
            "description": "Identificador estable en UPPER_SNAKE_CASE. Los clientes ramifican sobre este valor."
          },
          "legacyCode": {
            "type": "integer",
            "example": 40002,
            "description": "Código numérico legacy v5, solo cuando hay puente. Se omite en los códigos nacidos en v6."
          },
          "type": {
            "type": "string",
            "format": "uri",
            "example": "https://developers.hablame.co/docs/v6/errors/auth-required",
            "description": "URL a la documentación del error."
          },
          "message": {
            "type": "string",
            "description": "Texto legible en inglés. Es para el log del desarrollador, no para el usuario final: no lo parsees."
          },
          "details": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Contexto estructurado (validaciones, recurso afectado). Arreglo vacío cuando no hay contexto extra."
          }
        },
        "required": [
          "code",
          "type",
          "message",
          "details"
        ]
      },
      "ErrorEnvelope": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "properties": {
              "success": {
                "type": "boolean",
                "const": false
              },
              "error": {
                "$ref": "#/components/schemas/Error"
              }
            },
            "required": [
              "success",
              "error"
            ]
          }
        ],
        "description": "Respuesta de error. Toda respuesta con estado 4xx o 5xx tiene esta forma."
      },
      "Pagination": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "returned": {
            "type": "integer",
            "description": "Cuántos elementos trae esta página."
          },
          "hasMore": {
            "type": "boolean",
            "description": "`true` si hay más elementos después de esta página."
          }
        },
        "required": [
          "limit",
          "offset",
          "returned",
          "hasMore"
        ],
        "description": "Paginación por `limit`/`offset`. `hasMore` evita tener que contar el total."
      },
      "Country": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "example": "CO",
            "description": "ISO 3166-1 alpha-2 (2 letras MAYÚSCULA)."
          },
          "code3": {
            "type": "string",
            "example": "COL",
            "description": "ISO 3166-1 alpha-3."
          },
          "callingCode": {
            "type": "string",
            "example": "57",
            "description": "Indicativo E.164, sin `+`. Es string para no perder los ceros a la izquierda."
          },
          "mcc": {
            "type": "string",
            "example": "732",
            "description": "Mobile Country Code (E.212)."
          },
          "flag": {
            "type": "string",
            "format": "uri",
            "description": "URL de la bandera (SVG)."
          },
          "names": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Nombre del país por idioma (`en`, `es`). Presente solo cuando NO se envía `?lang`; un idioma sin nombre cargado no aparece."
          },
          "name": {
            "type": "string",
            "description": "Nombre del país en el `lang` pedido. Presente solo cuando se envía `?lang`."
          }
        },
        "required": [
          "code",
          "code3",
          "callingCode",
          "mcc",
          "flag"
        ]
      },
      "NumberInsightCountry": {
        "type": "object",
        "properties": {
          "iso2": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2."
          },
          "callingCode": {
            "type": "string",
            "description": "Indicativo E.164, sin `+`."
          },
          "name": {
            "type": "string"
          },
          "mcc": {
            "type": "string",
            "description": "Mobile Country Code (E.212)."
          }
        },
        "required": [
          "iso2",
          "callingCode",
          "name",
          "mcc"
        ]
      },
      "NumberInsightOperator": {
        "type": "object",
        "properties": {
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "brand": {
            "type": [
              "string",
              "null"
            ],
            "description": "Marca comercial. `null` en internacional."
          },
          "mnc": {
            "type": [
              "string",
              "null"
            ],
            "description": "Mobile Network Code. Siempre `null` cuando la línea no es `mobile` ni `m2m`."
          },
          "nrn": {
            "type": [
              "string",
              "null"
            ],
            "description": "Número de enrutamiento (Colombia). `null` en internacional."
          }
        },
        "required": [
          "name",
          "brand",
          "mnc",
          "nrn"
        ]
      },
      "NumberInsight": {
        "type": "object",
        "properties": {
          "phoneNumber": {
            "type": "object",
            "properties": {
              "e164": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Forma E.164, con `+`."
              },
              "national": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "raw": {
                "type": "string",
                "description": "El número tal como lo enviaste."
              }
            },
            "required": [
              "e164",
              "national",
              "raw"
            ]
          },
          "valid": {
            "type": "boolean"
          },
          "country": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/NumberInsightCountry"
              },
              {
                "type": "null"
              }
            ]
          },
          "lineType": {
            "type": [
              "string",
              "null"
            ],
            "description": "Tipo de línea (`mobile`, `landline`, `m2m`…). `null` si no se pudo determinar."
          },
          "numberType": {
            "type": [
              "string",
              "null"
            ]
          },
          "ported": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Si el número fue portado. Solo aplica a Colombia; `null` en internacional."
          },
          "operator": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/NumberInsightOperator"
              },
              {
                "type": "null"
              }
            ]
          },
          "area": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "region": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "municipality": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "department": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "daneCode": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Código DANE del municipio. Solo se llena en numeración fija de Colombia."
              }
            },
            "required": [
              "region",
              "municipality",
              "department",
              "daneCode"
            ]
          },
          "timezone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Zona horaria IANA. Solo se resuelve en números internacionales; `null` en Colombia."
          },
          "portability": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "donorOperator": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "brand": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "required": [
                  "name",
                  "brand"
                ]
              },
              "initialOperator": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "brand": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "required": [
                  "name",
                  "brand"
                ]
              },
              "activatedAt": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Fecha en que se activó la portación."
              },
              "processedAt": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "sourceDate": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "donorOperator",
              "initialOperator",
              "activatedAt",
              "processedAt",
              "sourceDate"
            ],
            "description": "Historia de portabilidad. Solo Colombia, y solo si el número fue portado."
          },
          "zone": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "id",
              "name"
            ],
            "description": "Zona estable del número (NRN del operador en móvil, municipio DANE en fijo). `null` en internacional y en fijos sin DANE."
          }
        },
        "required": [
          "phoneNumber",
          "valid",
          "country",
          "lineType",
          "numberType",
          "ported",
          "operator",
          "area",
          "timezone",
          "portability",
          "zone"
        ]
      },
      "Link": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Id numérico estable del enlace."
          },
          "code": {
            "type": "string",
            "description": "Código corto o alias (minúsculas). Único por dominio."
          },
          "domain": {
            "type": "string"
          },
          "shortUrl": {
            "type": "string",
            "format": "uri",
            "description": "URL corta completa (`https://{domain}/{code}`)."
          },
          "longUrl": {
            "type": "string",
            "format": "uri",
            "description": "URL de destino."
          },
          "alias": {
            "type": "boolean",
            "description": "`true` cuando el código lo eligió el usuario. Solo viaja en el detalle: el listado no incluye este campo."
          },
          "active": {
            "type": "boolean"
          },
          "expiresAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "clicks": {
            "type": "integer",
            "description": "Visitas totales (denormalizado; lo refresca el cron de drenado)."
          },
          "uniqueClicks": {
            "type": "integer"
          },
          "lastClickAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "code",
          "domain",
          "shortUrl",
          "longUrl",
          "active",
          "createdAt",
          "clicks",
          "uniqueClicks"
        ]
      },
      "Domain": {
        "type": "object",
        "properties": {
          "domain": {
            "type": "string"
          },
          "isDefault": {
            "type": "boolean"
          },
          "verified": {
            "type": "boolean"
          },
          "global": {
            "type": "boolean",
            "description": "`true` para dominios globales de la plataforma, `false` para los propios de tu cuenta."
          }
        },
        "required": [
          "domain",
          "isDefault",
          "verified",
          "global"
        ]
      },
      "Voice": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "Código de la voz; es lo que se envía en `voice`."
          },
          "name": {
            "type": "string"
          },
          "language": {
            "type": "string",
            "example": "es-US",
            "description": "Idioma y región (BCP-47)."
          },
          "accent": {
            "type": "string"
          },
          "gender": {
            "type": "string",
            "enum": [
              "female",
              "male"
            ]
          },
          "tier": {
            "type": "string",
            "enum": [
              "basic",
              "intermediate",
              "advanced"
            ]
          },
          "sampleRate": {
            "type": "integer",
            "description": "Frecuencia de muestreo en Hz."
          }
        },
        "required": [
          "code",
          "name",
          "language",
          "accent",
          "gender",
          "tier",
          "sampleRate"
        ]
      },
      "CallAttempt": {
        "type": "object",
        "properties": {
          "attempt": {
            "type": "integer",
            "description": "Número de intento, empezando en 1."
          },
          "status": {
            "type": "string",
            "enum": [
              "originating",
              "answered",
              "completed",
              "busy",
              "rejected",
              "no_answer",
              "failed"
            ],
            "description": "Estado del intento. `originating` y `answered` son transitorios."
          },
          "answered": {
            "type": "boolean"
          },
          "audioPlayed": {
            "type": "boolean"
          },
          "durationTotal": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Duración total del intento, en segundos."
          },
          "durationAir": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Duración en conversación, en segundos."
          },
          "hangupCause": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Código de causa de colgado (SIP/Asterisk)."
          },
          "causeText": {
            "type": [
              "string",
              "null"
            ]
          },
          "recorded": {
            "type": "boolean"
          },
          "dtmf": {
            "type": [
              "string",
              "null"
            ],
            "description": "Dígitos marcados por quien contestó."
          },
          "startedAt": {
            "type": [
              "string",
              "null"
            ],
            "example": "2026-06-23 15:04:06",
            "description": "Fecha y hora `YYYY-MM-DD HH:MM:SS` (sin zona; no es ISO-8601)."
          },
          "answeredAt": {
            "type": [
              "string",
              "null"
            ],
            "example": "2026-06-23 15:04:06",
            "description": "Fecha y hora `YYYY-MM-DD HH:MM:SS` (sin zona; no es ISO-8601)."
          },
          "endedAt": {
            "type": [
              "string",
              "null"
            ],
            "example": "2026-06-23 15:04:06",
            "description": "Fecha y hora `YYYY-MM-DD HH:MM:SS` (sin zona; no es ISO-8601)."
          }
        },
        "required": [
          "attempt",
          "status",
          "answered",
          "audioPlayed",
          "durationTotal",
          "durationAir",
          "hangupCause",
          "causeText",
          "recorded",
          "dtmf",
          "startedAt",
          "answeredAt",
          "endedAt"
        ]
      },
      "Call": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Identificador de la llamada (`cb_…`)."
          },
          "to": {
            "type": "string",
            "description": "Destino en formato internacional, solo dígitos."
          },
          "clientReference": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "success",
              "no_answer",
              "busy",
              "failed",
              "rejected",
              "voicemail",
              "canceled"
            ],
            "description": "Estado consolidado de la llamada."
          },
          "attemptsCount": {
            "type": "integer"
          },
          "attempts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CallAttempt"
            }
          },
          "createdAt": {
            "type": "string",
            "description": "Fecha y hora de creación, tal como la almacena la base."
          }
        },
        "required": [
          "id",
          "to",
          "clientReference",
          "status",
          "attemptsCount",
          "attempts",
          "createdAt"
        ]
      },
      "CallAudio": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Identificador del audio (ULID)."
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "originalName": {
            "type": [
              "string",
              "null"
            ]
          },
          "format": {
            "type": "string",
            "enum": [
              "wav",
              "mp3",
              "ogg",
              "m4a"
            ],
            "description": "Formato del archivo que subiste (no el normalizado interno)."
          },
          "durationMs": {
            "type": [
              "integer",
              "null"
            ]
          },
          "sizeBytes": {
            "type": "integer",
            "description": "Tamaño del audio ya normalizado."
          },
          "createdAt": {
            "type": "string",
            "description": "Fecha y hora de creación, tal como la almacena la base."
          }
        },
        "required": [
          "id",
          "name",
          "originalName",
          "format",
          "durationMs",
          "sizeBytes",
          "createdAt"
        ]
      },
      "Payment": {
        "type": "object",
        "properties": {
          "reference": {
            "type": "string",
            "description": "Referencia del pago."
          },
          "kind": {
            "type": "string",
            "enum": [
              "recharge",
              "invoice_payment"
            ],
            "description": "Recarga prepago o pago de factura postpago."
          },
          "amount": {
            "type": "string",
            "example": "108500.000000",
            "description": "Importe decimal con 6 decimales, en la moneda de la cuenta."
          },
          "currency": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "Estado del pago (`approved`, `pending`…)."
          },
          "method": {
            "type": "string"
          },
          "processor": {
            "type": "string"
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "example": "2026-05-18 12:30:00",
            "description": "Fecha y hora `YYYY-MM-DD HH:MM:SS` (sin zona; no es ISO-8601)."
          },
          "approvedAt": {
            "type": [
              "string",
              "null"
            ],
            "example": "2026-05-18 12:30:00",
            "description": "Fecha y hora `YYYY-MM-DD HH:MM:SS` (sin zona; no es ISO-8601)."
          },
          "expiresAt": {
            "type": [
              "string",
              "null"
            ],
            "example": "2026-05-18 12:30:00",
            "description": "Fecha y hora `YYYY-MM-DD HH:MM:SS` (sin zona; no es ISO-8601)."
          }
        },
        "required": [
          "reference",
          "kind",
          "amount",
          "currency",
          "status",
          "method",
          "processor",
          "createdAt",
          "approvedAt",
          "expiresAt"
        ]
      },
      "Adjustment": {
        "type": "object",
        "properties": {
          "reference": {
            "type": "string"
          },
          "direction": {
            "type": "string",
            "description": "Sentido del ajuste (`debit`, `credit`)."
          },
          "amount": {
            "type": "string",
            "example": "108500.000000",
            "description": "Importe decimal con 6 decimales, en la moneda de la cuenta."
          },
          "currency": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "effectiveDate": {
            "type": "string",
            "format": "date",
            "description": "Fecha en que el ajuste surte efecto (`YYYY-MM-DD`)."
          },
          "reason": {
            "type": "string"
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "example": "2026-05-18 12:30:00",
            "description": "Fecha y hora `YYYY-MM-DD HH:MM:SS` (sin zona; no es ISO-8601)."
          }
        },
        "required": [
          "reference",
          "direction",
          "amount",
          "currency",
          "category",
          "status",
          "effectiveDate",
          "reason",
          "createdAt"
        ]
      },
      "SpendLimit": {
        "type": "object",
        "properties": {
          "scopeType": {
            "type": "string",
            "description": "Ámbito del tope (`org`, `cost_center`, `api_key`, `service`, `user`)."
          },
          "scopeRef": {
            "type": "string",
            "description": "Identificador dentro del ámbito."
          },
          "amount": {
            "type": "string",
            "example": "108500.000000",
            "description": "Importe decimal con 6 decimales, en la moneda de la cuenta."
          },
          "period": {
            "type": "string",
            "description": "Periodicidad del tope (p. ej. `monthly`)."
          },
          "thresholds": {
            "type": "array",
            "items": {
              "type": "number"
            },
            "description": "Porcentajes de consumo en los que se dispara una alerta."
          }
        },
        "required": [
          "scopeType",
          "scopeRef",
          "amount",
          "period",
          "thresholds"
        ]
      },
      "UsageScope": {
        "type": "object",
        "properties": {
          "scopeType": {
            "type": "string"
          },
          "scopeRef": {
            "type": "string"
          },
          "consumed": {
            "type": "string",
            "example": "108500.000000",
            "description": "Importe decimal con 6 decimales, en la moneda de la cuenta."
          }
        },
        "required": [
          "scopeType",
          "scopeRef",
          "consumed"
        ]
      },
      "CostCenter": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "isDefault": {
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "code",
          "name",
          "status",
          "isDefault"
        ]
      },
      "AccountUser": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "integer"
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "roles": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Roles del usuario. Un usuario puede tener más de uno."
          },
          "costCenterId": {
            "type": "integer"
          },
          "status": {
            "type": "string"
          },
          "joinedAt": {
            "type": [
              "string",
              "null"
            ],
            "example": "2026-05-14 11:31:56",
            "description": "Fecha y hora `YYYY-MM-DD HH:MM:SS` (sin zona; no es ISO-8601)."
          },
          "lastUsedAt": {
            "type": [
              "string",
              "null"
            ],
            "example": "2026-05-14 11:31:56",
            "description": "Fecha y hora `YYYY-MM-DD HH:MM:SS` (sin zona; no es ISO-8601)."
          }
        },
        "required": [
          "userId",
          "firstName",
          "lastName",
          "email",
          "roles",
          "costCenterId",
          "status",
          "joinedAt",
          "lastUsedAt"
        ],
        "description": "Miembro de la organización. Contiene datos personales."
      },
      "StatusComponent": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "operational",
              "degraded",
              "partial_outage",
              "major_outage",
              "maintenance",
              "down",
              "unknown"
            ]
          }
        },
        "required": [
          "name",
          "status"
        ]
      },
      "Service": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "Código del servicio (`sms`, `tts`…)."
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "icon": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean",
            "description": "`true` si esta cuenta tiene acceso al servicio."
          }
        },
        "required": [
          "code",
          "name",
          "description",
          "icon",
          "enabled"
        ]
      },
      "PingSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "pong": {
                    "type": "boolean",
                    "const": true,
                    "description": "Siempre `true` cuando el llamante está autenticado correctamente."
                  },
                  "apiVersion": {
                    "type": "string",
                    "const": "v6",
                    "description": "Versión de la API que atendió la solicitud."
                  },
                  "client": {
                    "type": "object",
                    "properties": {
                      "ip": {
                        "type": "string",
                        "example": "203.0.113.45",
                        "description": "IP pública desde la que se originó el llamado."
                      }
                    },
                    "required": [
                      "ip"
                    ],
                    "description": "Datos del llamante que la API observa."
                  }
                },
                "required": [
                  "pong",
                  "apiVersion",
                  "client"
                ]
              }
            }
          }
        ]
      },
      "CountriesListSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Country"
                }
              }
            }
          }
        ]
      },
      "CountrySingleSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "$ref": "#/components/schemas/Country"
              }
            }
          }
        ]
      },
      "NumberInsightSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "$ref": "#/components/schemas/NumberInsight"
              }
            }
          }
        ]
      },
      "NumberInsightBatchSyncSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "results": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/NumberInsight"
                    },
                    "description": "Un resultado por número enviado, en el mismo orden (los repetidos se resuelven otra vez)."
                  }
                },
                "required": [
                  "results"
                ]
              }
            }
          }
        ]
      },
      "NumberInsightBatchQueuedSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "jobId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "status": {
                    "type": "string",
                    "const": "queued"
                  },
                  "count": {
                    "type": "integer",
                    "description": "Números aceptados en el trabajo."
                  },
                  "expiresAt": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Cuándo se purga el trabajo y su resultado."
                  }
                },
                "required": [
                  "jobId",
                  "status",
                  "count",
                  "expiresAt"
                ]
              }
            }
          }
        ]
      },
      "NumberInsightBatchJobSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "jobId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "queued",
                      "processing",
                      "done",
                      "failed"
                    ]
                  },
                  "count": {
                    "type": "integer"
                  },
                  "processed": {
                    "type": "integer",
                    "description": "Números resueltos hasta ahora."
                  },
                  "createdAt": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time"
                  },
                  "expiresAt": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time"
                  },
                  "download": {
                    "type": "object",
                    "properties": {
                      "url": {
                        "type": "string",
                        "format": "uri"
                      },
                      "format": {
                        "type": "string",
                        "example": "ndjson"
                      }
                    },
                    "required": [
                      "url",
                      "format"
                    ],
                    "description": "Solo cuando `status` es `done` y el archivo sigue disponible. La URL es firmada y temporal."
                  },
                  "error": {
                    "type": "string",
                    "description": "Motivo de la falla. Solo cuando `status` es `failed`. Es un texto dentro de `data`, no la envoltura de error."
                  }
                },
                "required": [
                  "jobId",
                  "status",
                  "count",
                  "processed",
                  "createdAt",
                  "expiresAt"
                ]
              }
            }
          }
        ]
      },
      "LinkSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "$ref": "#/components/schemas/Link"
              }
            }
          }
        ]
      },
      "LinkListSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "items": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Link"
                    },
                    "description": "Enlaces de la página. Las filas del listado no traen `alias`."
                  },
                  "page": {
                    "type": "integer"
                  },
                  "perPage": {
                    "type": "integer"
                  },
                  "total": {
                    "type": "integer"
                  },
                  "hasMore": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "items",
                  "page",
                  "perPage",
                  "total",
                  "hasMore"
                ]
              }
            }
          }
        ]
      },
      "DomainsListSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Domain"
                }
              }
            }
          }
        ]
      },
      "DeletedSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "deleted": {
                    "type": "string",
                    "description": "Código del enlace eliminado."
                  },
                  "domain": {
                    "type": "string"
                  }
                },
                "required": [
                  "deleted",
                  "domain"
                ]
              }
            }
          }
        ]
      },
      "LinkStatsSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string"
                  },
                  "domain": {
                    "type": "string"
                  },
                  "range": {
                    "type": "object",
                    "properties": {
                      "from": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "to": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "granularity": {
                        "type": "string",
                        "enum": [
                          "hour",
                          "day"
                        ]
                      }
                    },
                    "required": [
                      "from",
                      "to",
                      "granularity"
                    ]
                  },
                  "totals": {
                    "type": "object",
                    "properties": {
                      "clicks": {
                        "type": "integer"
                      },
                      "uniqueClicks": {
                        "type": "integer"
                      },
                      "firstClickAt": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "format": "date-time"
                      },
                      "lastClickAt": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "format": "date-time"
                      }
                    },
                    "required": [
                      "clicks",
                      "uniqueClicks",
                      "firstClickAt",
                      "lastClickAt"
                    ]
                  },
                  "series": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "ts": {
                          "type": "string",
                          "example": "2026-05-04",
                          "description": "Marca del bucket en `America/Bogota`: `YYYY-MM-DD` con granularidad `day`, `YYYY-MM-DD HH:00:00` (hora local, sin zona) con granularidad `hour`."
                        },
                        "clicks": {
                          "type": "integer"
                        },
                        "uniqueClicks": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "ts",
                        "clicks",
                        "uniqueClicks"
                      ]
                    }
                  },
                  "breakdowns": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string",
                            "description": "Valor del bucket, o `__other` para la cola larga más allá del top-N."
                          },
                          "clicks": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "key",
                          "clicks"
                        ]
                      }
                    },
                    "description": "Un arreglo top-N por cada dimensión pedida en `dimensions`."
                  }
                },
                "required": [
                  "code",
                  "domain",
                  "range",
                  "totals",
                  "series",
                  "breakdowns"
                ]
              }
            }
          }
        ]
      },
      "TtsSynthesizeSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Identificador de la síntesis (ULID)."
                  },
                  "status": {
                    "type": "string",
                    "const": "completed",
                    "description": "Siempre `completed`: la síntesis es sincrónica, un fallo devuelve error HTTP."
                  },
                  "cached": {
                    "type": "boolean",
                    "description": "`true` si se reutilizó un audio ya generado."
                  },
                  "charCount": {
                    "type": "integer"
                  },
                  "durationMs": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "voice": {
                    "type": "string",
                    "description": "Código de voz del catálogo que se usó."
                  },
                  "cost": {
                    "type": "string",
                    "example": "12.500000",
                    "description": "Costo de la generación: decimal con 6 decimales, como cadena."
                  },
                  "currency": {
                    "type": "string"
                  },
                  "audioUrl": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uri",
                    "description": "URL firmada y temporal del audio. `null` si no se pudo firmar."
                  }
                },
                "required": [
                  "id",
                  "status",
                  "cached",
                  "charCount",
                  "durationMs",
                  "voice",
                  "cost",
                  "currency",
                  "audioUrl"
                ]
              }
            }
          }
        ]
      },
      "TtsSynthesisSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "completed",
                      "failed"
                    ],
                    "description": "No hay estados intermedios: la síntesis es sincrónica."
                  },
                  "cached": {
                    "type": "boolean"
                  },
                  "charCount": {
                    "type": "integer"
                  },
                  "durationMs": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "language": {
                    "type": "string",
                    "example": "es-US"
                  },
                  "voice": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "tier": {
                    "type": "string",
                    "enum": [
                      "basic",
                      "intermediate",
                      "advanced"
                    ]
                  },
                  "cost": {
                    "type": "string",
                    "example": "12.500000",
                    "description": "Decimal con 6 decimales, como cadena."
                  },
                  "currency": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "audioUrl": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uri"
                  },
                  "reference": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Referencia externa del registro. Siempre `null` en las síntesis creadas por este API."
                  },
                  "errorCode": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Motivo del fallo cuando `status` es `failed`. No es un conjunto cerrado: puede traer texto del proveedor."
                  },
                  "createdAt": {
                    "type": "string",
                    "example": "2026-06-23 15:04:05",
                    "description": "Fecha y hora UTC `YYYY-MM-DD HH:MM:SS` (sin zona; no es ISO-8601)."
                  }
                },
                "required": [
                  "id",
                  "status",
                  "cached",
                  "charCount",
                  "durationMs",
                  "language",
                  "voice",
                  "tier",
                  "cost",
                  "currency",
                  "audioUrl",
                  "reference",
                  "errorCode",
                  "createdAt"
                ]
              }
            }
          }
        ]
      },
      "VoicesListSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "voices": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Voice"
                    }
                  }
                },
                "required": [
                  "voices"
                ]
              }
            }
          }
        ]
      },
      "CallQueuedSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Identificador de la llamada (`cb_…`); úsalo para consultarla."
                  },
                  "to": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "const": "queued",
                    "description": "Siempre `queued`: la llamada apenas entra a la cola."
                  },
                  "clientReference": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "required": [
                  "id",
                  "to",
                  "status",
                  "clientReference"
                ]
              }
            }
          }
        ]
      },
      "AudioCallQueuedSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "to": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "const": "queued"
                  },
                  "audioId": {
                    "type": "string",
                    "description": "Audio que se reproducirá: el que subiste o el `audioId` que enviaste."
                  },
                  "clientReference": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "required": [
                  "id",
                  "to",
                  "status",
                  "audioId",
                  "clientReference"
                ]
              }
            }
          }
        ]
      },
      "CallSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "$ref": "#/components/schemas/Call"
              }
            }
          }
        ]
      },
      "AudioUploadSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Identificador del audio; es lo que envías en `audioId`."
                  },
                  "durationMs": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "format": {
                    "type": "string",
                    "enum": [
                      "wav",
                      "mp3",
                      "ogg",
                      "m4a"
                    ],
                    "description": "Formato del archivo que subiste."
                  },
                  "sizeBytes": {
                    "type": "integer",
                    "description": "Tamaño del audio ya normalizado."
                  },
                  "cached": {
                    "type": "boolean",
                    "description": "`true` si el audio ya existía y se reutilizó en vez de crear otro."
                  }
                },
                "required": [
                  "id",
                  "durationMs",
                  "format",
                  "sizeBytes",
                  "cached"
                ]
              }
            }
          }
        ]
      },
      "AudiosListSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "audios": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/CallAudio"
                    }
                  }
                },
                "required": [
                  "audios"
                ]
              }
            }
          }
        ]
      },
      "BillingSummarySuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "account": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      },
                      "displayName": {
                        "type": "string"
                      },
                      "currency": {
                        "type": "string",
                        "description": "ISO 4217 (COP, USD, EUR)."
                      },
                      "status": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "id",
                      "displayName",
                      "currency",
                      "status"
                    ]
                  },
                  "billing": {
                    "type": "object",
                    "properties": {
                      "mode": {
                        "type": "string",
                        "enum": [
                          "prepaid",
                          "postpaid"
                        ]
                      },
                      "currency": {
                        "type": "string"
                      },
                      "period": {
                        "type": "string",
                        "example": "2026-06",
                        "description": "Mes natural `YYYY-MM`."
                      },
                      "available": {
                        "type": "string",
                        "example": "108500.000000",
                        "description": "Disponible: prepago `balance − consumed`; postpago `creditLimit − consumed`."
                      },
                      "balance": {
                        "type": "string",
                        "example": "108500.000000",
                        "description": "Lado crédito consolidado (recargas ± ajustes, con arrastre entre meses). Prepago."
                      },
                      "consumed": {
                        "type": "string",
                        "example": "108500.000000",
                        "description": "Consumo del periodo (consolidado + lo que va en vivo)."
                      },
                      "reserve": {
                        "type": "string",
                        "example": "108500.000000",
                        "description": "Reserva de seguridad prepago."
                      },
                      "creditLimit": {
                        "type": "string",
                        "example": "108500.000000",
                        "description": "Cupo de crédito mensual (postpago; `0.000000` en prepago)."
                      }
                    },
                    "required": [
                      "mode",
                      "currency",
                      "period",
                      "available",
                      "balance",
                      "consumed",
                      "reserve",
                      "creditLimit"
                    ],
                    "description": "Resumen del saldo. Todos los montos son cadenas decimales con 6 decimales, en `currency`."
                  }
                },
                "required": [
                  "account",
                  "billing"
                ]
              }
            }
          }
        ]
      },
      "UsageSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "period": {
                    "type": "string",
                    "example": "2026-06",
                    "description": "Periodo `YYYY-MM`."
                  },
                  "total": {
                    "type": "string",
                    "example": "108500.000000",
                    "description": "Gran total del periodo (el consumo de `org:0`)."
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/UsageScope"
                    },
                    "description": "Desglose por scope. Puede venir vacío."
                  }
                },
                "required": [
                  "period",
                  "total",
                  "scopes"
                ]
              }
            }
          }
        ]
      },
      "PaymentsListSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "items": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Payment"
                    }
                  },
                  "pagination": {
                    "$ref": "#/components/schemas/Pagination"
                  }
                },
                "required": [
                  "items",
                  "pagination"
                ]
              }
            }
          }
        ]
      },
      "AdjustmentsListSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "items": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Adjustment"
                    }
                  },
                  "pagination": {
                    "$ref": "#/components/schemas/Pagination"
                  }
                },
                "required": [
                  "items",
                  "pagination"
                ]
              }
            }
          }
        ]
      },
      "LimitsListSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "items": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/SpendLimit"
                    }
                  }
                },
                "required": [
                  "items"
                ]
              }
            }
          }
        ]
      },
      "OrganizationSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "integer"
                  },
                  "displayName": {
                    "type": "string"
                  },
                  "currency": {
                    "type": "string",
                    "description": "ISO 4217."
                  },
                  "billingMode": {
                    "type": "string",
                    "enum": [
                      "prepaid",
                      "postpaid"
                    ]
                  },
                  "status": {
                    "type": "string"
                  },
                  "blocks": {
                    "type": "object",
                    "properties": {
                      "general": {
                        "type": "boolean"
                      },
                      "collections": {
                        "type": "boolean"
                      },
                      "security": {
                        "type": "boolean"
                      }
                    },
                    "required": [
                      "general",
                      "collections",
                      "security"
                    ],
                    "description": "Bloqueos activos sobre la organización."
                  },
                  "onboardedAt": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "example": "2026-05-14 11:31:56",
                    "description": "Fecha y hora `YYYY-MM-DD HH:MM:SS` (sin zona; no es ISO-8601)."
                  }
                },
                "required": [
                  "id",
                  "displayName",
                  "currency",
                  "billingMode",
                  "status",
                  "blocks",
                  "onboardedAt"
                ]
              }
            }
          }
        ]
      },
      "CostCentersListSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "items": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/CostCenter"
                    }
                  }
                },
                "required": [
                  "items"
                ]
              }
            }
          }
        ]
      },
      "UsersListSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "items": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/AccountUser"
                    }
                  },
                  "pagination": {
                    "$ref": "#/components/schemas/Pagination"
                  }
                },
                "required": [
                  "items",
                  "pagination"
                ]
              }
            }
          }
        ]
      },
      "PlatformStatusSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "operational",
                      "degraded",
                      "partial_outage",
                      "major_outage",
                      "maintenance",
                      "down",
                      "unknown"
                    ],
                    "description": "Estado agregado de la plataforma."
                  },
                  "updatedAt": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time",
                    "description": "Cuándo se tomó la última muestra. `null` mientras no hay dato."
                  },
                  "stale": {
                    "type": "boolean",
                    "description": "`true` si el dato es viejo y no se pudo refrescar."
                  },
                  "components": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/StatusComponent"
                    }
                  },
                  "incidents": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string",
                          "description": "Texto del proveedor de estado, sin normalizar."
                        },
                        "startedAt": {
                          "type": "string",
                          "description": "Texto del proveedor de estado; el formato no está garantizado."
                        }
                      },
                      "required": [
                        "name",
                        "status",
                        "startedAt"
                      ]
                    }
                  },
                  "maintenance": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "startsAt": {
                          "type": "string",
                          "description": "Texto del proveedor de estado; el formato no está garantizado."
                        },
                        "endsAt": {
                          "type": "string",
                          "description": "Texto del proveedor de estado; el formato no está garantizado."
                        }
                      },
                      "required": [
                        "name",
                        "startsAt",
                        "endsAt"
                      ]
                    }
                  }
                },
                "required": [
                  "status",
                  "updatedAt",
                  "stale",
                  "components",
                  "incidents",
                  "maintenance"
                ]
              }
            }
          }
        ]
      },
      "ServicesListSuccess": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "success": {
                "const": true
              },
              "data": {
                "type": "object",
                "properties": {
                  "items": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Service"
                    }
                  }
                },
                "required": [
                  "items"
                ]
              }
            }
          }
        ]
      }
    },
    "headers": {
      "RateLimit-Limit": {
        "description": "Cupo para la ventana activa.",
        "schema": {
          "type": "integer",
          "example": 20
        }
      },
      "RateLimit-Remaining": {
        "description": "Solicitudes restantes en la ventana activa.",
        "schema": {
          "type": "integer",
          "example": 19
        }
      },
      "RateLimit-Reset": {
        "description": "Segundos hasta que la ventana se reinicie. NO es una marca de tiempo Unix.",
        "schema": {
          "type": "integer",
          "example": 31
        }
      },
      "RateLimit-Policy": {
        "description": "Política activa de límite por endpoint.",
        "schema": {
          "type": "string",
          "example": "20;w=60;name=\"endpoint\""
        }
      },
      "Retry-After": {
        "description": "Segundos a esperar antes de reintentar.",
        "schema": {
          "type": "integer",
          "example": 31
        }
      },
      "Idempotency-Status": {
        "description": "Solo cuando enviaste `Idempotency-Key`: `created` si la operación se ejecutó, `replayed` si se devolvió la respuesta guardada.",
        "schema": {
          "type": "string",
          "enum": [
            "created",
            "replayed"
          ]
        }
      },
      "Idempotency-Replayed": {
        "description": "Presente y en `true` solo cuando la respuesta es una repetición guardada.",
        "schema": {
          "type": "string",
          "enum": [
            "true"
          ]
        }
      }
    },
    "examples": {
      "AuthRequired": {
        "summary": "No se envió un token Bearer y cada llamada lo requiere. (AUTH_REQUIRED)",
        "value": {
          "success": false,
          "error": {
            "code": "AUTH_REQUIRED",
            "legacyCode": 40002,
            "type": "https://developers.hablame.co/docs/v6/errors/auth-required",
            "message": "Authentication is required. Send your API key as `Authorization: Bearer <token>`.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "AuthInvalidKey": {
        "summary": "La API key no se reconoce: formato equivocado, revocada o vencida. (AUTH_INVALID_KEY)",
        "value": {
          "success": false,
          "error": {
            "code": "AUTH_INVALID_KEY",
            "legacyCode": 40003,
            "type": "https://developers.hablame.co/docs/v6/errors/auth-invalid-key",
            "message": "The provided API key is invalid or has expired. Please verify the value sent in the `Authorization: Bearer` header.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "AuthCostCenterDisabled": {
        "summary": "El centro de costo asociado a la key está deshabilitado. (AUTH_COST_CENTER_DISABLED)",
        "value": {
          "success": false,
          "error": {
            "code": "AUTH_COST_CENTER_DISABLED",
            "type": "https://developers.hablame.co/docs/v6/errors/auth-cost-center-disabled",
            "message": "The cost center linked to this API key is disabled.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "AuthServiceNotAllowed": {
        "summary": "La key no tiene permitido el servicio detrás de este endpoint. (AUTH_SERVICE_NOT_ALLOWED)",
        "value": {
          "success": false,
          "error": {
            "code": "AUTH_SERVICE_NOT_ALLOWED",
            "type": "https://developers.hablame.co/docs/v6/errors/auth-service-not-allowed",
            "message": "This API key is not authorized to use the requested service.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "AuthSourceIpUnknown": {
        "summary": "Reservado: solo como puente desde v5. (AUTH_SOURCE_IP_UNKNOWN)",
        "value": {
          "success": false,
          "error": {
            "code": "AUTH_SOURCE_IP_UNKNOWN",
            "legacyCode": 40015,
            "type": "https://developers.hablame.co/docs/v6/errors/auth-source-ip-unknown",
            "message": "Internal error has occurred, the source IP could not be discovered.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "AccountNotActive": {
        "summary": "Tu organización está suspendida o cerrada. (ACCOUNT_NOT_ACTIVE)",
        "value": {
          "success": false,
          "error": {
            "code": "ACCOUNT_NOT_ACTIVE",
            "type": "https://developers.hablame.co/docs/v6/errors/account-not-active",
            "message": "The organization associated with this API key is not active. Please contact support.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "AccountBlocked": {
        "summary": "Hay un bloqueo total activo: cada llamada se rechaza. (ACCOUNT_BLOCKED)",
        "value": {
          "success": false,
          "error": {
            "code": "ACCOUNT_BLOCKED",
            "type": "https://developers.hablame.co/docs/v6/errors/account-blocked",
            "message": "The organization is currently blocked. Please contact support.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "AccountReadOnly": {
        "summary": "Modo solo lectura: solo pasan GET, HEAD y OPTIONS. (ACCOUNT_READ_ONLY)",
        "value": {
          "success": false,
          "error": {
            "code": "ACCOUNT_READ_ONLY",
            "type": "https://developers.hablame.co/docs/v6/errors/account-read-only",
            "message": "The organization is in read-only mode. Only safe HTTP methods (GET, HEAD, OPTIONS) are permitted.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "AccountConfigNotFound": {
        "summary": "No pudimos cargar la organización de tu key. (ACCOUNT_CONFIG_NOT_FOUND)",
        "value": {
          "success": false,
          "error": {
            "code": "ACCOUNT_CONFIG_NOT_FOUND",
            "legacyCode": 4002,
            "type": "https://developers.hablame.co/docs/v6/errors/account-config-not-found",
            "message": "Organization configuration could not be loaded. Please contact support.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "RateDdosExceeded": {
        "summary": "Volumen inusualmente alto desde tu IP de origen. (RATE_DDOS_EXCEEDED)",
        "value": {
          "success": false,
          "error": {
            "code": "RATE_DDOS_EXCEEDED",
            "legacyCode": 40001,
            "type": "https://developers.hablame.co/docs/v6/errors/rate-ddos-exceeded",
            "message": "Too many requests from this IP within a short window. Please wait before retrying.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "RateTpsExceeded": {
        "summary": "Excediste el cupo de tu organización para este endpoint. (RATE_TPS_EXCEEDED)",
        "value": {
          "success": false,
          "error": {
            "code": "RATE_TPS_EXCEEDED",
            "legacyCode": 40001,
            "type": "https://developers.hablame.co/docs/v6/errors/rate-tps-exceeded",
            "message": "You have exceeded the allowed request rate for this endpoint. Please wait before retrying.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "IdempotencyKeyInvalid": {
        "summary": "La llave de idempotencia no cumple el formato. (IDEMPOTENCY_KEY_INVALID)",
        "value": {
          "success": false,
          "error": {
            "code": "IDEMPOTENCY_KEY_INVALID",
            "type": "https://developers.hablame.co/docs/v6/errors/idempotency-key-invalid",
            "message": "The `Idempotency-Key` header is invalid. Use 1-255 characters limited to letters, digits, hyphen or underscore.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "IdempotencyInProgress": {
        "summary": "La primera petición con esa llave sigue en proceso. (IDEMPOTENCY_IN_PROGRESS)",
        "value": {
          "success": false,
          "error": {
            "code": "IDEMPOTENCY_IN_PROGRESS",
            "type": "https://developers.hablame.co/docs/v6/errors/idempotency-in-progress",
            "message": "A request with this `Idempotency-Key` is still being processed. Please retry shortly.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "IdempotencyKeyReused": {
        "summary": "La llave ya existe, pero con un cuerpo distinto. (IDEMPOTENCY_KEY_REUSED)",
        "value": {
          "success": false,
          "error": {
            "code": "IDEMPOTENCY_KEY_REUSED",
            "type": "https://developers.hablame.co/docs/v6/errors/idempotency-key-reused",
            "message": "This `Idempotency-Key` was already used with a different request body. Use a new key for a different operation.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "UrlInvalid": {
        "summary": "La URL de destino está mal formada. (URL_INVALID)",
        "value": {
          "success": false,
          "error": {
            "code": "URL_INVALID",
            "type": "https://developers.hablame.co/docs/v6/errors/url-invalid",
            "message": "The destination URL is not valid. Provide an absolute `http`/`https` URL with a host.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "UrlSchemeNotAllowed": {
        "summary": "Solo se admiten destinos http y https. (URL_SCHEME_NOT_ALLOWED)",
        "value": {
          "success": false,
          "error": {
            "code": "URL_SCHEME_NOT_ALLOWED",
            "type": "https://developers.hablame.co/docs/v6/errors/url-scheme-not-allowed",
            "message": "Only `http` and `https` destination URLs are allowed.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "UrlTooLong": {
        "summary": "La URL de destino supera el largo máximo. (URL_TOO_LONG)",
        "value": {
          "success": false,
          "error": {
            "code": "URL_TOO_LONG",
            "type": "https://developers.hablame.co/docs/v6/errors/url-too-long",
            "message": "The destination URL exceeds the maximum allowed length.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "UrlBlockedHost": {
        "summary": "El host de destino es privado o reservado. (URL_BLOCKED_HOST)",
        "value": {
          "success": false,
          "error": {
            "code": "URL_BLOCKED_HOST",
            "type": "https://developers.hablame.co/docs/v6/errors/url-blocked-host",
            "message": "The destination host is not allowed (private, reserved, or loopback addresses are blocked).",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "AliasInvalid": {
        "summary": "El alias no cumple el formato permitido. (ALIAS_INVALID)",
        "value": {
          "success": false,
          "error": {
            "code": "ALIAS_INVALID",
            "type": "https://developers.hablame.co/docs/v6/errors/alias-invalid",
            "message": "The alias must be 3-32 characters of lowercase letters, digits, hyphen or underscore.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "AliasTaken": {
        "summary": "El alias ya está en uso en este dominio. (ALIAS_TAKEN)",
        "value": {
          "success": false,
          "error": {
            "code": "ALIAS_TAKEN",
            "type": "https://developers.hablame.co/docs/v6/errors/alias-taken",
            "message": "That alias is already in use on this domain.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "AliasReserved": {
        "summary": "El alias es una palabra reservada. (ALIAS_RESERVED)",
        "value": {
          "success": false,
          "error": {
            "code": "ALIAS_RESERVED",
            "type": "https://developers.hablame.co/docs/v6/errors/alias-reserved",
            "message": "That alias is reserved and cannot be used.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "ExpiresAtInvalid": {
        "summary": "`expiresAt` debe ser una fecha ISO futura. (EXPIRES_AT_INVALID)",
        "value": {
          "success": false,
          "error": {
            "code": "EXPIRES_AT_INVALID",
            "type": "https://developers.hablame.co/docs/v6/errors/expires-at-invalid",
            "message": "`expiresAt` must be a future ISO-8601 date-time.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "LinkNotFound": {
        "summary": "No hay un enlace con ese dominio y código. (LINK_NOT_FOUND)",
        "value": {
          "success": false,
          "error": {
            "code": "LINK_NOT_FOUND",
            "type": "https://developers.hablame.co/docs/v6/errors/link-not-found",
            "message": "No short link was found for that domain and code.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "NothingToUpdate": {
        "summary": "No se envió ningún campo actualizable. (NOTHING_TO_UPDATE)",
        "value": {
          "success": false,
          "error": {
            "code": "NOTHING_TO_UPDATE",
            "type": "https://developers.hablame.co/docs/v6/errors/nothing-to-update",
            "message": "No updatable fields were provided.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "CodeGenerationFailed": {
        "summary": "No se pudo asignar un código único. (CODE_GENERATION_FAILED)",
        "value": {
          "success": false,
          "error": {
            "code": "CODE_GENERATION_FAILED",
            "type": "https://developers.hablame.co/docs/v6/errors/code-generation-failed",
            "message": "Could not generate a unique short code. Please retry.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "StatsRangeInvalid": {
        "summary": "El rango o la granularidad de estadísticas son inválidos. (STATS_RANGE_INVALID)",
        "value": {
          "success": false,
          "error": {
            "code": "STATS_RANGE_INVALID",
            "type": "https://developers.hablame.co/docs/v6/errors/stats-range-invalid",
            "message": "The requested statistics range or granularity is invalid.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "DomainNotAllowed": {
        "summary": "El dominio elegido no está disponible para tu cuenta. (DOMAIN_NOT_ALLOWED)",
        "value": {
          "success": false,
          "error": {
            "code": "DOMAIN_NOT_ALLOWED",
            "type": "https://developers.hablame.co/docs/v6/errors/domain-not-allowed",
            "message": "The requested domain is not available for this account.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "CountryNotFound": {
        "summary": "No existe un país con ese código. (COUNTRY_NOT_FOUND)",
        "value": {
          "success": false,
          "error": {
            "code": "COUNTRY_NOT_FOUND",
            "type": "https://developers.hablame.co/docs/v6/errors/country-not-found",
            "message": "No country was found with the supplied code. Use the ISO 3166-1 alpha-2 form (e.g. `CO`, `US`).",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "InfraDbConnectionError": {
        "summary": "Un servicio interno estuvo momentáneamente inalcanzable. (INFRA_DB_CONNECTION_ERROR)",
        "value": {
          "success": false,
          "error": {
            "code": "INFRA_DB_CONNECTION_ERROR",
            "legacyCode": 40011,
            "type": "https://developers.hablame.co/docs/v6/errors/infra-db-connection-error",
            "message": "Could not establish connection to the database. Please retry; if the error persists, contact support.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "InfraCacheUnavailable": {
        "summary": "Un componente interno está temporalmente no disponible. (INFRA_CACHE_UNAVAILABLE)",
        "value": {
          "success": false,
          "error": {
            "code": "INFRA_CACHE_UNAVAILABLE",
            "type": "https://developers.hablame.co/docs/v6/errors/infra-cache-unavailable",
            "message": "A backing cache is temporarily unavailable. Please retry shortly.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "InfraDbQueryTimeout": {
        "summary": "Una consulta interna tardó demasiado y se cortó. (INFRA_DB_QUERY_TIMEOUT)",
        "value": {
          "success": false,
          "error": {
            "code": "INFRA_DB_QUERY_TIMEOUT",
            "type": "https://developers.hablame.co/docs/v6/errors/infra-db-query-timeout",
            "message": "The upstream database took too long to respond. Please retry shortly.",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      },
      "InternalServerError": {
        "summary": "Algo salió mal de nuestro lado. (INTERNAL_SERVER_ERROR)",
        "value": {
          "success": false,
          "error": {
            "code": "INTERNAL_SERVER_ERROR",
            "type": "https://developers.hablame.co/docs/v6/errors/internal-server-error",
            "message": "Internal Server Error",
            "details": []
          },
          "meta": {
            "requestId": "b9b1704baffab21150213c02fd853975",
            "timestamp": "2026-06-04T19:43:59+00:00",
            "responseTimeMs": 0.8
          }
        }
      }
    },
    "x-errorCatalog": [
      {
        "code": "AUTH_REQUIRED",
        "status": 401,
        "domain": "auth",
        "legacyCode": 40002,
        "summary": "No se envió un token Bearer y cada llamada lo requiere.",
        "url": "https://developers.hablame.co/docs/errors#auth-required"
      },
      {
        "code": "AUTH_INVALID_KEY",
        "status": 401,
        "domain": "auth",
        "legacyCode": 40003,
        "summary": "La API key no se reconoce: formato equivocado, revocada o vencida.",
        "url": "https://developers.hablame.co/docs/errors#auth-invalid-key"
      },
      {
        "code": "AUTH_COST_CENTER_DISABLED",
        "status": 401,
        "domain": "auth",
        "summary": "El centro de costo asociado a la key está deshabilitado.",
        "url": "https://developers.hablame.co/docs/errors#auth-cost-center-disabled"
      },
      {
        "code": "AUTH_SERVICE_NOT_ALLOWED",
        "status": 401,
        "domain": "auth",
        "summary": "La key no tiene permitido el servicio detrás de este endpoint.",
        "url": "https://developers.hablame.co/docs/errors#auth-service-not-allowed"
      },
      {
        "code": "AUTH_SOURCE_IP_UNKNOWN",
        "status": 400,
        "domain": "auth",
        "legacyCode": 40015,
        "summary": "Reservado: solo como puente desde v5.",
        "url": "https://developers.hablame.co/docs/errors#auth-source-ip-unknown"
      },
      {
        "code": "ACCOUNT_NOT_ACTIVE",
        "status": 403,
        "domain": "account",
        "summary": "Tu organización está suspendida o cerrada.",
        "url": "https://developers.hablame.co/docs/errors#account-not-active"
      },
      {
        "code": "ACCOUNT_BLOCKED",
        "status": 403,
        "domain": "account",
        "summary": "Hay un bloqueo total activo: cada llamada se rechaza.",
        "url": "https://developers.hablame.co/docs/errors#account-blocked"
      },
      {
        "code": "ACCOUNT_READ_ONLY",
        "status": 403,
        "domain": "account",
        "summary": "Modo solo lectura: solo pasan GET, HEAD y OPTIONS.",
        "url": "https://developers.hablame.co/docs/errors#account-read-only"
      },
      {
        "code": "ACCOUNT_CONFIG_NOT_FOUND",
        "status": 403,
        "domain": "account",
        "legacyCode": 4002,
        "summary": "No pudimos cargar la organización de tu key.",
        "url": "https://developers.hablame.co/docs/errors#account-config-not-found"
      },
      {
        "code": "RATE_DDOS_EXCEEDED",
        "status": 429,
        "domain": "rate",
        "legacyCode": 40001,
        "summary": "Volumen inusualmente alto desde tu IP de origen.",
        "url": "https://developers.hablame.co/docs/errors#rate-ddos-exceeded"
      },
      {
        "code": "RATE_TPS_EXCEEDED",
        "status": 429,
        "domain": "rate",
        "legacyCode": 40001,
        "summary": "Excediste el cupo de tu organización para este endpoint.",
        "url": "https://developers.hablame.co/docs/errors#rate-tps-exceeded"
      },
      {
        "code": "IDEMPOTENCY_KEY_INVALID",
        "status": 400,
        "domain": "idempotency",
        "summary": "La llave de idempotencia no cumple el formato.",
        "url": "https://developers.hablame.co/docs/errors#idempotency-key-invalid"
      },
      {
        "code": "IDEMPOTENCY_IN_PROGRESS",
        "status": 409,
        "domain": "idempotency",
        "summary": "La primera petición con esa llave sigue en proceso.",
        "url": "https://developers.hablame.co/docs/errors#idempotency-in-progress"
      },
      {
        "code": "IDEMPOTENCY_KEY_REUSED",
        "status": 422,
        "domain": "idempotency",
        "summary": "La llave ya existe, pero con un cuerpo distinto.",
        "url": "https://developers.hablame.co/docs/errors#idempotency-key-reused"
      },
      {
        "code": "URL_INVALID",
        "status": 400,
        "domain": "urlshortener",
        "summary": "La URL de destino está mal formada.",
        "url": "https://developers.hablame.co/docs/errors#url-invalid"
      },
      {
        "code": "URL_SCHEME_NOT_ALLOWED",
        "status": 400,
        "domain": "urlshortener",
        "summary": "Solo se admiten destinos http y https.",
        "url": "https://developers.hablame.co/docs/errors#url-scheme-not-allowed"
      },
      {
        "code": "URL_TOO_LONG",
        "status": 400,
        "domain": "urlshortener",
        "summary": "La URL de destino supera el largo máximo.",
        "url": "https://developers.hablame.co/docs/errors#url-too-long"
      },
      {
        "code": "URL_BLOCKED_HOST",
        "status": 400,
        "domain": "urlshortener",
        "summary": "El host de destino es privado o reservado.",
        "url": "https://developers.hablame.co/docs/errors#url-blocked-host"
      },
      {
        "code": "ALIAS_INVALID",
        "status": 400,
        "domain": "urlshortener",
        "summary": "El alias no cumple el formato permitido.",
        "url": "https://developers.hablame.co/docs/errors#alias-invalid"
      },
      {
        "code": "ALIAS_TAKEN",
        "status": 409,
        "domain": "urlshortener",
        "summary": "El alias ya está en uso en este dominio.",
        "url": "https://developers.hablame.co/docs/errors#alias-taken"
      },
      {
        "code": "ALIAS_RESERVED",
        "status": 409,
        "domain": "urlshortener",
        "summary": "El alias es una palabra reservada.",
        "url": "https://developers.hablame.co/docs/errors#alias-reserved"
      },
      {
        "code": "EXPIRES_AT_INVALID",
        "status": 400,
        "domain": "urlshortener",
        "summary": "`expiresAt` debe ser una fecha ISO futura.",
        "url": "https://developers.hablame.co/docs/errors#expires-at-invalid"
      },
      {
        "code": "LINK_NOT_FOUND",
        "status": 404,
        "domain": "urlshortener",
        "summary": "No hay un enlace con ese dominio y código.",
        "url": "https://developers.hablame.co/docs/errors#link-not-found"
      },
      {
        "code": "NOTHING_TO_UPDATE",
        "status": 400,
        "domain": "urlshortener",
        "summary": "No se envió ningún campo actualizable.",
        "url": "https://developers.hablame.co/docs/errors#nothing-to-update"
      },
      {
        "code": "CODE_GENERATION_FAILED",
        "status": 500,
        "domain": "urlshortener",
        "summary": "No se pudo asignar un código único.",
        "url": "https://developers.hablame.co/docs/errors#code-generation-failed"
      },
      {
        "code": "STATS_RANGE_INVALID",
        "status": 400,
        "domain": "urlshortener",
        "summary": "El rango o la granularidad de estadísticas son inválidos.",
        "url": "https://developers.hablame.co/docs/errors#stats-range-invalid"
      },
      {
        "code": "DOMAIN_NOT_ALLOWED",
        "status": 400,
        "domain": "urlshortener",
        "summary": "El dominio elegido no está disponible para tu cuenta.",
        "url": "https://developers.hablame.co/docs/errors#domain-not-allowed"
      },
      {
        "code": "COUNTRY_NOT_FOUND",
        "status": 404,
        "domain": "tools",
        "summary": "No existe un país con ese código.",
        "url": "https://developers.hablame.co/docs/errors#country-not-found"
      },
      {
        "code": "INFRA_DB_CONNECTION_ERROR",
        "status": 503,
        "domain": "infra",
        "legacyCode": 40011,
        "summary": "Un servicio interno estuvo momentáneamente inalcanzable.",
        "url": "https://developers.hablame.co/docs/errors#infra-db-connection-error"
      },
      {
        "code": "INFRA_CACHE_UNAVAILABLE",
        "status": 503,
        "domain": "infra",
        "summary": "Un componente interno está temporalmente no disponible.",
        "url": "https://developers.hablame.co/docs/errors#infra-cache-unavailable"
      },
      {
        "code": "INFRA_DB_QUERY_TIMEOUT",
        "status": 504,
        "domain": "infra",
        "summary": "Una consulta interna tardó demasiado y se cortó.",
        "url": "https://developers.hablame.co/docs/errors#infra-db-query-timeout"
      },
      {
        "code": "BAD_REQUEST",
        "status": 400,
        "domain": "generic",
        "summary": "No pudimos interpretar tu petición.",
        "url": "https://developers.hablame.co/docs/errors#bad-request"
      },
      {
        "code": "UNAUTHORIZED",
        "status": 401,
        "domain": "generic",
        "summary": "401 genérico: prefiere los códigos `AUTH_*` cuando estén.",
        "url": "https://developers.hablame.co/docs/errors#unauthorized"
      },
      {
        "code": "FORBIDDEN",
        "status": 403,
        "domain": "generic",
        "summary": "403 genérico: alternativa cuando no aplica un `ACCOUNT_*`.",
        "url": "https://developers.hablame.co/docs/errors#forbidden"
      },
      {
        "code": "NOT_FOUND",
        "status": 404,
        "domain": "generic",
        "summary": "La ruta que pediste no existe en esta API.",
        "url": "https://developers.hablame.co/docs/errors#not-found"
      },
      {
        "code": "METHOD_NOT_ALLOWED",
        "status": 405,
        "domain": "generic",
        "summary": "La ruta existe pero no acepta este método HTTP.",
        "url": "https://developers.hablame.co/docs/errors#method-not-allowed"
      },
      {
        "code": "TOO_MANY_REQUESTS",
        "status": 429,
        "domain": "generic",
        "summary": "429 genérico: prefiere los códigos `RATE_*` cuando estén.",
        "url": "https://developers.hablame.co/docs/errors#too-many-requests"
      },
      {
        "code": "INTERNAL_SERVER_ERROR",
        "status": 500,
        "domain": "generic",
        "summary": "Algo salió mal de nuestro lado.",
        "url": "https://developers.hablame.co/docs/errors#internal-server-error"
      },
      {
        "code": "SERVICE_UNAVAILABLE",
        "status": 503,
        "domain": "generic",
        "summary": "503 genérico: un servicio del que dependemos no responde.",
        "url": "https://developers.hablame.co/docs/errors#service-unavailable"
      },
      {
        "code": "GATEWAY_TIMEOUT",
        "status": 504,
        "domain": "generic",
        "summary": "504 genérico: un servicio del que dependemos tardó demasiado.",
        "url": "https://developers.hablame.co/docs/errors#gateway-timeout"
      }
    ]
  }
}