5.1 Custom APIs - Creación de Acciones Personalizadas

Crea endpoints personalizados con Custom APIs

Hasta ahora, hemos registrado plugins para eventos estándar como Create, Update y Delete. Pero ¿qué pasa cuando necesitas exponer funcionalidad personalizada que no encaja en ningún evento existente? Para eso existen las Custom APIs: te permiten crear tus propios endpoints que pueden ser llamados desde código, Power Automate, o cualquier cliente de Web API.

Objetivos de aprendizaje

  • Entender cómo funcionan las Custom APIs y cuándo usarlas
  • Crear Custom APIs con parámetros de entrada y salida
  • Implementar plugins asociados a Custom APIs
  • Llamar Custom APIs desde C# y Web API

El problema que resuelven las Custom APIs

Imagina que tienes una lógica de negocio compleja: calcular el precio final de un pedido considerando descuentos por cliente, promociones activas, inventario disponible, y costes de envío. Esta lógica necesita ejecutarse desde varios lugares: desde un formulario de Power Apps, desde Power Automate cuando llega un pedido por email, desde una integración con tu sitio web.

Podrías duplicar la lógica en cada lugar, pero eso sería un mantenimiento pesadilla. Lo ideal es tener un único punto de entrada que ejecute la lógica y devuelva el resultado.

Las Custom APIs son exactamente eso: endpoints personalizados que puedes definir y que se exponen automáticamente en la Web API de Dataverse. Son la evolución moderna de las antiguas "Custom Actions" de workflows.


Custom API vs Custom Action

Si vienes de versiones anteriores de Dynamics, quizás conozcas las Custom Actions que se creaban desde la interfaz de workflows. Las Custom APIs las mejoran en casi todos los aspectos:

Mejor rendimiento: Las Custom APIs se definen como registros de datos, no como workflows. No hay capa de workflow de por medio.

Mejor soporte ALM: Las Custom APIs viajan perfectamente en soluciones y son más fáciles de versionar y desplegar.

Functions (GET): Las Custom Actions solo soportaban POST. Las Custom APIs pueden ser "Functions" que responden a GET, lo que es más apropiado para operaciones que solo leen datos.

Binding flexible: Puedes definir si la API está vinculada a una entidad específica o es global.

En resumen: para desarrollo nuevo, siempre usa Custom APIs. Las Custom Actions solo tienen sentido para mantener sistemas legacy.


Creando una Custom API paso a paso

Vamos a crear una Custom API que calcula el descuento final para un cliente basándose en su nivel de fidelidad y el monto de compra.

Paso 1: Crear el registro de Custom API

Las Custom APIs se definen como registros en la entidad "customapi". Puedes crearlos desde el Maker Portal (Settings → Custom APIs) o mediante código. Los campos principales son:

Unique Name: El nombre técnico que usarás para llamar la API, por ejemplo "new_CalcularDescuentoCliente".

Display Name: Nombre legible para administradores.

Description: Documentación de lo que hace la API.

Binding Type: Si es global (Unbound), de entidad (Entity), o de colección (EntityCollection).

Is Function: True si solo lee datos (permitirá llamar con GET). False si modifica datos (requiere POST).

Allowed Custom Processing Step Type: Determina si se pueden añadir plugins de terceros a tu API.

Paso 2: Definir parámetros de entrada

Cada parámetro de entrada es un registro en "customapirequestparameter". Nuestra API necesita:

  • ClienteId (EntityReference): referencia a la cuenta del cliente
  • MontoCompra (Decimal): el monto de la compra

Para cada parámetro defines: nombre, tipo, si es requerido, y descripción.

Paso 3: Definir parámetros de salida

Los parámetros de salida se definen en "customapiresponseproperty". Nuestra API devolverá:

  • PorcentajeDescuento (Decimal): el porcentaje de descuento aplicado
  • MontoFinal (Decimal): el monto después del descuento

Paso 4: Implementar el plugin

El plugin asociado a una Custom API se registra igual que cualquier otro, pero el mensaje es el Unique Name de tu API.


public class CalcularDescuentoPlugin : IPlugin
{
    public void Execute(IServiceProvider serviceProvider)
    {
        var trace = (ITracingService)
            serviceProvider.GetService(typeof(ITracingService));
        var context = (IPluginExecutionContext)
            serviceProvider.GetService(typeof(IPluginExecutionContext));
        var factory = (IOrganizationServiceFactory)
            serviceProvider.GetService(typeof(IOrganizationServiceFactory));
        
        IOrganizationService service = factory.CreateOrganizationService(context.UserId);
        
        // Obtener parámetros de entrada
        EntityReference clienteRef = (EntityReference)context.InputParameters["ClienteId"];
        decimal montoCompra = (decimal)context.InputParameters["MontoCompra"];
        
        trace.Trace($"Calculando descuento para cliente {clienteRef.Id}, monto: {montoCompra}");
        
        // Obtener datos del cliente
        Entity cliente = service.Retrieve("account", clienteRef.Id, 
            new ColumnSet("customerlevel", "loyaltypoints"));
        
        int nivelCliente = cliente.GetAttributeValue("customerlevel")?.Value ?? 1;
        
        // Calcular descuento según nivel
        decimal porcentaje = nivelCliente switch
        {
            1 => 0m,    // Bronze: sin descuento
            2 => 5m,    // Silver: 5%
            3 => 10m,   // Gold: 10%
            4 => 15m,   // Platinum: 15%
            _ => 0m
        };
        
        // Descuento adicional por volumen
        if (montoCompra > 10000)
        {
            porcentaje += 5m;
            trace.Trace("Descuento adicional por volumen aplicado");
        }
        
        decimal montoFinal = montoCompra * (1 - porcentaje / 100);
        
        trace.Trace($"Resultado: {porcentaje}% descuento, monto final: {montoFinal}");
        
        // Establecer parámetros de salida
        context.OutputParameters["PorcentajeDescuento"] = porcentaje;
        context.OutputParameters["MontoFinal"] = montoFinal;
    }
}

Observa que los parámetros de entrada vienen de context.InputParameters y los de salida se establecen en context.OutputParameters. Los nombres deben coincidir exactamente con los que definiste en los registros de parámetros.


Llamando a la Custom API

Una vez creada y desplegada, la Custom API está disponible para ser llamada desde múltiples lugares.

Desde C# (SDK)


// Crear la request con el nombre de la Custom API
OrganizationRequest request = new OrganizationRequest("new_CalcularDescuentoCliente");

// Establecer parámetros de entrada
request["ClienteId"] = new EntityReference("account", cuentaId);
request["MontoCompra"] = 7500m;

// Ejecutar
OrganizationResponse response = service.Execute(request);

// Leer parámetros de salida
decimal descuento = (decimal)response["PorcentajeDescuento"];
decimal montoFinal = (decimal)response["MontoFinal"];

Console.WriteLine($"Descuento: {descuento}%, Final: {montoFinal:C}");

Desde Web API


POST /api/data/v9.2/new_CalcularDescuentoCliente
Content-Type: application/json

{
  "ClienteId": {
    "@odata.type": "Microsoft.Dynamics.CRM.account",
    "accountid": "abc-123-..."
  },
  "MontoCompra": 7500.00
}

La respuesta vendrá en formato JSON con los parámetros de salida.

Desde Power Automate

En Power Automate, las Custom APIs aparecen como acciones disponibles dentro del conector de Dataverse. Solo selecciona "Perform an unbound action" y elige tu Custom API de la lista.


Consideraciones de diseño

Function vs Action: Si tu API solo lee datos sin modificar nada, márcala como Function. Esto permite llamarla con GET y da señales claras a consumidores sobre su comportamiento.

Binding: Si la API siempre opera sobre un registro específico (calcular totales de un pedido), considera hacer binding a esa entidad. Esto hace la API más intuitiva de usar.

Versionamiento: Las Custom APIs son difíciles de versionar una vez publicadas. Piensa bien en los parámetros antes de publicar en producción.

Manejo de errores: Lanza InvalidPluginExecutionException con mensajes claros. Estos se convierten en respuestas de error HTTP que los consumidores recibirán.


Puntos clave

  • Custom APIs son endpoints personalizados que se exponen en Web API automáticamente
  • Son la forma moderna de crear operaciones personalizadas (reemplazan Custom Actions)
  • Los parámetros se definen como registros separados (input/output)
  • El plugin usa InputParameters y OutputParameters del contexto
  • Se pueden llamar desde C#, Web API, Power Automate, y cualquier cliente HTTP

Para profundizar

Inicia sesión e inscríbete para guardar tu progreso.
En este curso
¿Te ha resultado útil?