Ciudades: catálogo DANE
La ciudad de contactos, empresas y de la organización se guarda con el código oficial DANE/DIVIPOLA del municipio. Así "BOGOTA", "Bogotá D.C." y "bogota" son el mismo lugar, los filtros por ciudad o departamento funcionan y el dato sirve tal cual para facturación electrónica y envíos.
Cómo se guarda una ciudad
El catálogo tiene los 1.122 municipios y los 33 departamentos de Colombia (Bogotá D.C. cuenta como departamento, código 11). Cuando envías un cityCode válido, FusionCRM escribe por ti el nombre de la ciudad, el departamento y el código del departamento.
- • Por API, formularios públicos y MCP la ciudad es estricta: o se resuelve sin dudas contra el catálogo, o la petición falla con los candidatos para que elijas.
- • En la app y en el importador de Excel es flexible: un texto que no se reconoce se guarda igual como ciudad sin confirmar (
citycon valor ycityCodevacío) y aparece en la lista dinámica "Ciudades sin confirmar". - • Solo Colombia: si el contacto tiene un
countrydistinto de Colombia,cityse guarda como texto libre sin código ni validación. - • FusionCRM nunca elige un "mejor candidato" por ti: 66 municipios comparten nombre (La Unión, Rionegro, Armenia, Villanueva…).
Los cuatro campos
| Campo | Qué es | Quién lo escribe |
|---|---|---|
| cityCode | Código DANE del municipio, 5 dígitos (05001 = Medellín, 76001 = Cali, 11001 = Bogotá). | Es la fuente de verdad. Lo envías tú. |
| city | Nombre corto del municipio ("Medellín"). | Se deriva de cityCode. Si solo mandas city, el servidor busca el código. |
| state | Departamento en texto ("Antioquia"). | Se deriva de cityCode. Enviado solo sirve de pista para desambiguar city. |
| stateCode | Código DANE del departamento, 2 dígitos (05 = Antioquia). | Siempre lo calcula el servidor: el valor que envíes se ignora. |
Los cuatro llegan en la respuesta de contactos y empresas. Para buscar o filtrar usa siempre los códigos: el texto puede venir con otra grafía en datos antiguos.
Catálogo: municipios y departamentos
Dos endpoints de solo lectura que acepta cualquier token válido, sin permiso especial:
- •
GET /catalogs/municipalities— sin parámetros devuelve el catálogo completo. Conqbusca por nombre (sin tildes ni mayúsculas, capitales primero, 20 resultados por defecto);stateacota a un departamento ("05") ylimitva de 1 a 1200. - •
GET /catalogs/states— los 33 departamentos con código, nombre oficial, nombre corto e ISO 3166-2.
Buscar el código de una ciudad
curl "https://api.fusioncol.com/api/catalogs/municipalities?q=rionegro" \ -H "Authorization: Bearer <token>"
Respuesta: dos municipios con el mismo nombre; mira stateName antes de elegir
{
"success": true,
"version": "divipola-2024-12-30.b17287ee",
"count": 2,
"data": [
{ "code": "05615", "name": "Rionegro", "shortName": "Rionegro",
"stateCode": "05", "stateName": "Antioquia", "stateIso2": "ANT", "dane8": "05615000" },
{ "code": "68615", "name": "Rionegro", "shortName": "Rionegro",
"stateCode": "68", "stateName": "Santander", "stateIso2": "SAN", "dane8": "68615000" }
]
}code es el valor que va en cityCode. shortName es lo que el CRM muestra y guarda en city. dane8 es el código de la cabecera municipal (code+ "000"), el que piden algunas transportadoras.
Todos los municipios de un departamento
curl "https://api.fusioncol.com/api/catalogs/municipalities?state=05" \ -H "Authorization: Bearer <token>"
Caché y ETag
El catálogo cambia muy poco (se regenera cuando el DANE publica un corte nuevo). Las respuestas traen Cache-Control: public, max-age=86400 y un ETag con la versión del catálogo. Descárgalo una vez, búscalo en local y revalida con If-None-Match: mientras no cambie recibes 304 sin cuerpo.
Revalidar la copia local
curl -i "https://api.fusioncol.com/api/catalogs/municipalities" \ -H "Authorization: Bearer <token>" \ -H 'If-None-Match: "<etag de la respuesta anterior>"' # HTTP/1.1 304 Not Modified
Crear y actualizar con ciudad
En POST y PUT de /contacts y /companies tienes tres formas de indicar la ciudad, de mejor a peor:
- 1.
cityCode: gana sobre cualquier texto y deriva todo lo demás. - 2.
city+state: el departamento desambigua homónimos (Rionegro + Santander → 68615). - 3. Solo
city: funciona si el nombre es único y está bien escrito; si no, 422.
Crear un contacto con el código DANE
curl -X POST "https://api.fusioncol.com/api/contacts" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"firstName": "Ana",
"email": "ana@example.com",
"cityCode": "76001"
}'
# Guarda city "Cali", state "Valle del Cauca" y stateCode "76"Si el body no trae city, cityCode ni state, la ubicación guardada no se toca. En PUT /contacts/{id}, un city vacío ("") limpia la ciudad, el código y el departamento derivado.
Errores de ciudad
| HTTP | code | Cuándo |
|---|---|---|
| 422 | CITY_UNRESOLVED | city es ambigua (Rionegro existe en Antioquia y en Santander), está mal escrita (Medelin) o no existe. No se guarda nada; candidates trae los municipios posibles. |
| 400 | INVALID_CITY_CODE | El cityCode no existe en el catálogo. candidates llega vacío. |
| 400 | STATE_UNKNOWN / LIMIT_INVALID | Solo en /catalogs/municipalities: state no es un departamento o limit está fuera de 1-1200. |
422 CITY_UNRESOLVED: reenvía con uno de los candidates como cityCode
{
"success": false,
"error": "CITY_UNRESOLVED",
"code": "CITY_UNRESOLVED",
"message": "La ciudad \"Rionegro\" corresponde a varios municipios; indique cityCode o el departamento",
"input": "Rionegro",
"status": "ambiguous",
"candidates": [
{ "code": "05615", "shortName": "Rionegro", "stateName": "Antioquia", "stateCode": "05" },
{ "code": "68615", "shortName": "Rionegro", "stateName": "Santander", "stateCode": "68" }
]
}status dice por qué falló: ambiguous (varios municipios), fuzzy (parecido pero no exacto, como "Medelin"), none (nada parecido; candidates vacío) o invalid_code (el 400 de un cityCode inexistente).
422 INVALID_CITY, con otra forma de cuerpo (reason, input como objeto y candidatos con dane8). Está explicado en su página.Formularios, importación y filtros
- • Formularios web (
POST /forms/{id}/submity el formulario público): la ciudad es estricta. Una ciudad no resuelta responde 400 (no 422) concode: CITY_UNRESOLVED, un mensaje para mostrarle al visitante y loscandidates; el contacto no se crea. Un campodepartamento,depto,dptooestadose toma comostatey ayuda con los homónimos. - • Importación por API (
POST /import/contacts):locationMode: "lenient"(por defecto, igual que el Excel de la app) guarda las ciudades no reconocidas como sin confirmar y las resume enwarnings;"strict"rechaza esas filas enrejectedRowscon los candidatos enreason. Prueba primero condryRun: true. - • Filtrar contactos o empresas: por municipio
?cityCode=05001&matchMode=exact; por departamento entero?stateCode=05&matchMode=exact. Repetir la clave hace un OR (?cityCode=05001&cityCode=76001). - • MCP: los tools de contactos y empresas aceptan
cityCodey devuelven los mismos candidatos; ver MCP.
city con valor y cityCodevacío (la lista "Ciudades sin confirmar" ya lo hace en la app), resuelve cada ciudad con /catalogs/municipalities?q= y actualiza con PUT /contacts/{id} mandando solo cityCode.