{
  "openapi": "3.1.0",
  "info": {
    "title": "Aliantic API",
    "version": "1.0.0",
    "description": "API pública de Aliantic sobre tu grafo de relaciones B2B.\n\nDescribe SOLO lo que existe y está estable hoy (`/v1/relationships`, `/v1/contacts`). Si una capacidad no figura acá, no está: preferimos un contrato chico y cierto antes que uno grande y aspiracional.\n\nMisma credencial que el MCP: un personal access token que se emite en Configuración → Cuenta → Conectar con IA. Los tokens son de solo lectura por defecto; para escribir hay que emitir uno con permiso de escritura a propósito.\n\nPara consumir Aliantic desde un asistente de IA hay además un servidor MCP: https://aliantic.ar/api/ai/mcp (con token) y https://aliantic.ar/api/ai/public/mcp (sin token, solo contenido público).",
    "contact": { "name": "Aliantic", "url": "https://aliantic.ar/desarrolladores" }
  },
  "servers": [{ "url": "https://aliantic.ar/api", "description": "Producción" }],
  "security": [{ "bearerAuth": [] }],
  "tags": [
    { "name": "Relaciones", "description": "Tu ecosistema: clientes, proveedores, aliados." }
  ],
  "paths": {
    "/v1/relationships": {
      "get": {
        "tags": ["Relaciones"],
        "summary": "Listar tus relaciones",
        "description": "Devuelve las relaciones del negocio activo con su salud calculada. La salud sale de la cadencia esperada y de la última señal real de actividad, no solo del último contacto registrado a mano.",
        "operationId": "listRelationships",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Máximo de resultados. Se recorta al rango permitido.",
            "schema": { "type": "integer", "minimum": 1, "maximum": 200, "default": 50 }
          }
        ],
        "responses": {
          "200": {
            "description": "Listado de relaciones.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["relationships"],
                  "properties": {
                    "relationships": { "type": "array", "items": { "$ref": "#/components/schemas/Relationship" } }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/NoAutenticado" },
          "500": { "$ref": "#/components/responses/ErrorInterno" }
        }
      },
      "post": {
        "tags": ["Relaciones"],
        "summary": "Crear una relación",
        "description": "Crea una relación en tu ecosistema. Requiere un token con permiso de escritura.\n\nEs idempotente por nombre: si ya existe una relación con ese nombre devuelve 409 con el `id` de la que ya está, en vez de duplicar el nodo. Un cliente que reintenta puede tratar el 409 como éxito.",
        "operationId": "createRelationship",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["name"],
                "properties": {
                  "name": { "type": "string", "minLength": 2, "description": "Nombre de la empresa." },
                  "type": { "$ref": "#/components/schemas/TipoRelacion" },
                  "notes": { "type": "string", "description": "Contexto libre." }
                }
              },
              "example": { "name": "Distribuidora del Sur", "type": "proveedor" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Creada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": { "ok": { "type": "boolean" }, "relationship": { "type": "object" } }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/DatosInvalidos" },
          "401": { "$ref": "#/components/responses/NoAutenticado" },
          "403": { "$ref": "#/components/responses/SoloLectura" },
          "409": {
            "description": "Ya existe una relación con ese nombre. Trae el `id` de la existente.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": { "error": { "type": "string" }, "id": { "type": "string", "format": "uuid" } }
                }
              }
            }
          },
          "500": { "$ref": "#/components/responses/ErrorInterno" }
        }
      }
    },
    "/v1/contacts": {
      "post": {
        "tags": ["Relaciones"],
        "summary": "Registrar un contacto con una relación",
        "description": "Deja asentado que hablaste con esa empresa hoy. Es la acción que mantiene viva la salud de la relación.\n\nSe identifica por `id` o por `name`: mandá uno de los dos. Requiere token con permiso de escritura.",
        "operationId": "logContact",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Mandá `id` o `name`, al menos uno.",
                "properties": {
                  "id": { "type": "string", "format": "uuid", "description": "Id de la relación." },
                  "name": { "type": "string", "description": "Nombre exacto, si no tenés el id." },
                  "note": { "type": "string", "maxLength": 500, "description": "De qué hablaron." }
                },
                "anyOf": [{ "required": ["id"] }, { "required": ["name"] }]
              },
              "example": { "name": "Distribuidora del Sur", "note": "Cotizaron el pedido de septiembre" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Contacto registrado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": { "type": "boolean" },
                    "relationship": { "type": "string" },
                    "lastContactAt": { "type": "string", "format": "date-time" }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/DatosInvalidos" },
          "401": { "$ref": "#/components/responses/NoAutenticado" },
          "403": { "$ref": "#/components/responses/SoloLectura" },
          "404": { "$ref": "#/components/responses/NoEncontrado" },
          "500": { "$ref": "#/components/responses/ErrorInterno" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Personal access token de Aliantic. Se emite en Configuración → Cuenta → Conectar con IA. Solo lectura por defecto."
      }
    },
    "schemas": {
      "TipoRelacion": {
        "type": "string",
        "description": "Qué es esa empresa para vos.",
        "enum": ["cliente", "proveedor", "aliado", "partner", "distribuidor", "inversor", "sponsor", "comunidad", "freelancer", "otro"]
      },
      "EstadoSalud": {
        "type": "string",
        "description": "healthy: dentro de la cadencia · attention: se está pasando · at_risk: muy fría · untracked: nunca registraste contacto.",
        "enum": ["healthy", "attention", "at_risk", "untracked"]
      },
      "Relationship": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "name": { "type": "string" },
          "type": { "type": "string", "description": "Etiqueta legible del tipo." },
          "importance": { "type": "string", "enum": ["alta", "media", "baja"] },
          "health": { "$ref": "#/components/schemas/EstadoSalud" },
          "daysSinceContact": {
            "type": ["integer", "null"],
            "description": "Días desde la última señal. `null` si nunca hubo ninguna."
          },
          "cadenceDays": { "type": "integer", "description": "Cada cuántos días esperás hablar con esta empresa." }
        }
      },
      "Error": {
        "type": "object",
        "properties": { "error": { "type": "string", "description": "Mensaje en español, mostrable al usuario." } }
      }
    },
    "responses": {
      "NoAutenticado": {
        "description": "Falta el token o no es válido.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "SoloLectura": {
        "description": "El token es de solo lectura. Emitir uno con permiso de escritura.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "DatosInvalidos": {
        "description": "El body no cumple el contrato.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "NoEncontrado": {
        "description": "No existe esa relación en tu ecosistema.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "ErrorInterno": {
        "description": "Error del servidor.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    }
  }
}
