Integración con MuleSoft Anypoint
Construye flujos de conectividad API-led en MuleSoft Anypoint Platform que transforman eventos empresariales en registros Certyo anclados en blockchain usando DataWeave y flujos Mule 4.
Descripción general
MuleSoft Anypoint Platform es la plataforma de integración líder en la industria para conectividad API-led. Esta guía muestra cómo posicionar Certyo como una System API dentro de la arquitectura de tres capas de MuleSoft, permitiendo que cualquier sistema empresarial ancle registros en la blockchain a través de una capa de proceso estandarizada.
La integración utiliza el HTTP Connector de Mule 4 y DataWeave 2.0 para transformar las cargas útiles de tus sistemas fuente al formato de registro de Certyo, y luego llama a POST /api/v1/records de forma asíncrona.
Requisitos previos
- Cuenta de MuleSoft Anypoint Platform (Trial o Enterprise)
- Anypoint Studio 7.x IDE instalado localmente
- Clave API de Certyo (obtener de la Guía de inicio rápido)
- Java 8 u 11 (requerido por Anypoint Studio)
- Acceso a las APIs de tu sistema ERP/CRM fuente
Arquitectura
El patrón de conectividad API-led de MuleSoft organiza las integraciones en tres capas. Certyo se ubica en la capa de System API, proporcionando anclaje blockchain como un servicio reutilizable.
┌─────────────────────────────────────────────────────────────────┐
│ EXPERIENCE LAYER │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Mobile App │ │ Web Portal │ │ Partner Dashboard │ │
│ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────┘ │
│ └─────────────────┼─────────────────────┘ │
├───────────────────────────┼─────────────────────────────────────┤
│ PROCESS LAYER │
│ ┌────────────────────────┴────────────────────────────────┐ │
│ │ Certyo Record Ingestion Process API │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ 1. Receive event from Experience / System API │ │ │
│ │ │ 2. DataWeave: transform to Certyo record format │ │ │
│ │ │ 3. Enrich with tenant metadata │ │ │
│ │ │ 4. Route to Certyo System API │ │ │
│ │ │ 5. Return record hash to caller │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ └────────────────────────┬────────────────────────────────┘ │
├───────────────────────────┼─────────────────────────────────────┤
│ SYSTEM LAYER │
│ ┌─────────────┐ ┌──────┴──────┐ ┌──────────────────────┐ │
│ │ SAP S/4 │ │ Certyo │ │ Salesforce CRM │ │
│ │ System API │ │ System API │ │ System API │ │
│ │ │ │ (Records) │ │ │ │
│ └─────────────┘ └─────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Data Flow:
Source System ──► MuleSoft Process API ──► Certyo System API
(DataWeave transform) (POST /api/v1/records)
│
202 Accepted + hash
│
Kafka ──► Accumulate
──► Merkle ──► IPFS
──► Polygon anchorCapas de conectividad API-Led
- System APIs — Conexiones directas a sistemas backend. Certyo expone una System API para la ingesta de registros (
POST /api/v1/records) y verificación (POST /api/v1/verify/record). Tus sistemas ERP, CRM y bases de datos tienen cada uno sus propias System APIs. - Process APIs — Orquestan la lógica de negocio entre System APIs. La Process API de Certyo transforma las cargas fuente al formato de registro de Certyo, enriquece con metadatos del tenant, maneja errores y enruta al endpoint correcto.
- Experience APIs — Adaptadas para consumidores específicos (móvil, web, socios). Estas llaman a la Process API sin necesitar conocer el formato de registro de Certyo.
Configuración de flujo Mule 4
El siguiente flujo Mule 4 recibe un HTTP POST, transforma la carga útil con DataWeave y llama al API de Certyo. Este es el núcleo de tu implementación de Process API.
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
xmlns:http="http://www.mulesoft.org/schema/mule/http"
xmlns:ee="http://www.mulesoft.org/schema/mule/ee/core"
xmlns:secure-properties="http://www.mulesoft.org/schema/mule/secure-properties"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.mulesoft.org/schema/mule/core
http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/http
http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/ee/core
http://www.mulesoft.org/schema/mule/ee/core/current/mule-ee.xsd
http://www.mulesoft.org/schema/mule/secure-properties
http://www.mulesoft.org/schema/mule/secure-properties/current/mule-secure-properties.xsd">
<!-- Load encrypted properties (API keys, credentials) -->
<secure-properties:config name="secureConfig"
file="secure-${env}.yaml"
key="${secure.key}" />
<!-- Certyo API HTTP configuration -->
<http:request-config name="certyo-api-config">
<http:request-connection
host="www.certyos.com"
port="443"
protocol="HTTPS">
<http:default-headers>
<http:default-header
key="X-API-Key"
value="${secure::certyo.api.key}" />
<http:default-header
key="Content-Type"
value="application/json" />
</http:default-headers>
</http:request-connection>
</http:request-config>
<!-- Inbound HTTP listener for Process API -->
<http:listener-config name="process-api-listener">
<http:listener-connection host="0.0.0.0" port="8081" />
</http:listener-config>
<!-- Main flow: Receive event, transform, send to Certyo -->
<flow name="certyo-record-ingestion-flow">
<http:listener config-ref="process-api-listener"
path="/api/certyo/ingest"
method="POST" />
<!-- Transform source payload to Certyo record format -->
<ee:transform>
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output application/json
---
{
tenantId: vars.tenantId default p('certyo.tenant.id'),
database: payload.sourceSystem default "erp",
collection: payload.entityType default "records",
recordId: payload.id as String,
recordVersion: payload.version default "1",
operationType: payload.operation default "upsert",
recordPayload: payload.data,
sourceTimestamp: payload.timestamp
default now() as String {format: "yyyy-MM-dd'T'HH:mm:ss'Z'"},
idempotencyKey: payload.id ++ "-" ++ (payload.version default "1")
++ "-" ++ now() as String {format: "yyyy-MM-dd"}
}]]></ee:set-payload>
</ee:message>
</ee:transform>
<!-- Call Certyo Records API -->
<http:request config-ref="certyo-api-config"
method="POST"
path="/api/v1/records" />
<!-- Log the accepted response -->
<logger level="INFO"
message="Record ingested: #[payload.recordHash]" />
</flow>
</mule>Transformación DataWeave
DataWeave 2.0 es el lenguaje de expresiones de MuleSoft para transformar datos entre formatos. A continuación se muestran ejemplos de transformación de cargas ERP comunes al formato de registro de Certyo, además de un script Python equivalente para equipos que prefieren probar transformaciones fuera de Anypoint Studio.
%dw 2.0
output application/json
// Map ERP-specific field names to Certyo's canonical record format.
// This transform handles SAP, D365, and generic ERP payloads.
var sourceSystem = payload.sourceSystem default "unknown"
var entityMap = {
"SAP": { db: "sap-s4", collection: "material-documents" },
"D365": { db: "dynamics-365", collection: "sales-orders" },
"ERP": { db: payload.databaseName default "erp", collection: payload.tableName default "records" }
}
var mapping = entityMap[sourceSystem] default entityMap["ERP"]
---
{
tenantId: p('certyo.tenant.id'),
database: mapping.db,
collection: mapping.collection,
recordId: payload.documentNumber as String,
recordVersion: payload.version default "1",
operationType: if (payload.isNew == true) "insert"
else if (payload.isDeleted == true) "delete"
else "upsert",
recordPayload: {
documentNumber: payload.documentNumber,
documentDate: payload.documentDate,
(plant: payload.plant) if sourceSystem == "SAP",
(businessUnit: payload.businessUnit) if sourceSystem == "D365",
lineItems: payload.items map (item) -> {
itemId: item.itemNumber as String,
material: item.materialCode default item.productId,
quantity: item.quantity as Number,
unit: item.unitOfMeasure default "EA",
amount: item.netAmount as Number default 0
},
totalAmount: sum(payload.items.netAmount default [0]),
currency: payload.currency default "USD"
},
sourceTimestamp: payload.changedAt
default payload.createdAt
default now() as String {format: "yyyy-MM-dd'T'HH:mm:ss'Z'"},
idempotencyKey: payload.documentNumber ++ "-"
++ (payload.version default "1") ++ "-"
++ now() as String {format: "yyyy-MM-dd"}
}"""
Certyo record transformer — equivalent to the DataWeave 2.0 script.
Use this for testing transformations outside Anypoint Studio
or as a standalone microservice alternative.
"""
import os
import json
from datetime import datetime, timezone
from typing import Any
TENANT_ID = os.environ.get("CERTYO_TENANT_ID", "acme-corp")
ENTITY_MAP = {
"SAP": {"db": "sap-s4", "collection": "material-documents"},
"D365": {"db": "dynamics-365", "collection": "sales-orders"},
}
def transform_to_certyo_record(payload: dict[str, Any]) -> dict[str, Any]:
"""Transform an ERP payload into a Certyo record."""
source = payload.get("sourceSystem", "ERP")
mapping = ENTITY_MAP.get(source, {
"db": payload.get("databaseName", "erp"),
"collection": payload.get("tableName", "records"),
})
items = payload.get("items", [])
line_items = [
{
"itemId": str(item.get("itemNumber", "")),
"material": item.get("materialCode") or item.get("productId", ""),
"quantity": float(item.get("quantity", 0)),
"unit": item.get("unitOfMeasure", "EA"),
"amount": float(item.get("netAmount", 0)),
}
for item in items
]
record_payload: dict[str, Any] = {
"documentNumber": payload["documentNumber"],
"documentDate": payload.get("documentDate"),
"lineItems": line_items,
"totalAmount": sum(i["amount"] for i in line_items),
"currency": payload.get("currency", "USD"),
}
# Add source-specific fields
if source == "SAP" and "plant" in payload:
record_payload["plant"] = payload["plant"]
if source == "D365" and "businessUnit" in payload:
record_payload["businessUnit"] = payload["businessUnit"]
now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
version = payload.get("version", "1")
doc_number = str(payload["documentNumber"])
op = "upsert"
if payload.get("isNew"):
op = "insert"
elif payload.get("isDeleted"):
op = "delete"
return {
"tenantId": TENANT_ID,
"database": mapping["db"],
"collection": mapping["collection"],
"recordId": doc_number,
"recordVersion": version,
"operationType": op,
"recordPayload": record_payload,
"sourceTimestamp": payload.get("changedAt")
or payload.get("createdAt")
or now,
"idempotencyKey": f"{doc_number}-{version}-{today}",
}
if __name__ == "__main__":
sample = {
"sourceSystem": "SAP",
"documentNumber": "4900012345",
"documentDate": "2026-04-14",
"version": "1",
"plant": "1000",
"currency": "EUR",
"items": [
{"itemNumber": 10, "materialCode": "MAT-001",
"quantity": 100, "unitOfMeasure": "KG", "netAmount": 5000.00},
{"itemNumber": 20, "materialCode": "MAT-002",
"quantity": 50, "unitOfMeasure": "EA", "netAmount": 2500.00},
],
}
print(json.dumps(transform_to_certyo_record(sample), indent=2))# Send a test event to your MuleSoft Process API
# (running locally on port 8081)
curl -X POST http://localhost:8081/api/certyo/ingest \
-H "Content-Type: application/json" \
-d '{
"sourceSystem": "SAP",
"documentNumber": "4900012345",
"documentDate": "2026-04-14",
"version": "1",
"plant": "1000",
"currency": "EUR",
"isNew": true,
"items": [
{
"itemNumber": 10,
"materialCode": "MAT-001",
"quantity": 100,
"unitOfMeasure": "KG",
"netAmount": 5000.00
},
{
"itemNumber": 20,
"materialCode": "MAT-002",
"quantity": 50,
"unitOfMeasure": "EA",
"netAmount": 2500.00
}
]
}'
# Expected response (proxied from Certyo):
# {
# "recordId": "4900012345",
# "recordHash": "a1b2c3d4...",
# "tenantId": "acme-corp",
# "acceptedAt": "2026-04-14T10:30:00.000Z",
# "idempotencyReplayed": false
# }Manejo de errores
Los flujos Mule en producción deben manejar fallos transitorios (timeouts de red, límites de tasa 429) y fallos permanentes (400 bad request, 401 unauthorized) de manera diferente. El flujo a continuación agrega lógica de reintento y una cola de mensajes muertos para errores irrecuperables.
<!-- Add inside <flow name="certyo-record-ingestion-flow"> -->
<error-handler>
<!-- Retry on transient errors: 429, 500, 502, 503, 504, timeouts -->
<on-error-continue
type="HTTP:TIMEOUT, HTTP:CONNECTIVITY, HTTP:RETRY_EXHAUSTED"
enableNotifications="true">
<until-successful
maxRetries="3"
millisBetweenRetries="2000">
<http:request config-ref="certyo-api-config"
method="POST"
path="/api/v1/records" />
</until-successful>
</on-error-continue>
<!-- Handle rate limiting (429) with exponential backoff -->
<on-error-continue
type="HTTP:TOO_MANY_REQUESTS"
enableNotifications="true">
<set-variable variableName="retryAfter"
value="#[attributes.headers.'Retry-After' default '5']" />
<logger level="WARN"
message="Rate limited. Retry after #[vars.retryAfter]s" />
<!-- Wait and retry -->
<until-successful
maxRetries="5"
millisBetweenRetries="#[vars.retryAfter as Number * 1000]">
<http:request config-ref="certyo-api-config"
method="POST"
path="/api/v1/records" />
</until-successful>
</on-error-continue>
<!-- Dead letter queue for permanent failures -->
<on-error-propagate
type="HTTP:BAD_REQUEST, HTTP:UNAUTHORIZED, HTTP:FORBIDDEN">
<logger level="ERROR"
message="Permanent failure: #[error.description]" />
<ee:transform>
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output application/json
---
{
originalPayload: vars.originalPayload,
error: error.description,
errorType: error.errorType.identifier,
timestamp: now() as String {format: "yyyy-MM-dd'T'HH:mm:ss'Z'"},
flowName: flow.name
}]]></ee:set-payload>
</ee:message>
</ee:transform>
<!-- Publish to dead letter queue for manual review -->
<jms:publish config-ref="activemq-config"
destination="certyo.dlq" />
</on-error-propagate>
</error-handler>Autenticación
La integración utiliza dos conjuntos de credenciales:
- MuleSoft Anypoint Connected App — Para desplegar y gestionar tus aplicaciones Mule a través de las APIs de Anypoint Platform (CI/CD). Se configura en tu cuenta de Anypoint Platform en Access Management > Connected Apps.
- Clave API de Certyo — Se envía como el encabezado
X-API-Keyen cada solicitud a Certyo. Almacénala en el archivo Secure Properties de Mule, nunca en texto plano.
Configuración de Secure Properties
# Generate an encrypted value using Anypoint Studio's
# Secure Properties Tool (or the CLI):
#
# mule-credential-vault-cli encrypt \
# --algorithm AES --mode CBC \
# --key "${MULE_SECURE_KEY}" \
# --value "your-certyo-api-key-here"
certyo:
api:
key: "![encryptedValue]"
tenant:
id: "acme-corp"
# Reference in your flow XML:
# ${secure::certyo.api.key}
# ${secure::certyo.tenant.id}Conector personalizado de MuleSoft
Para equipos que desean un conector reutilizable y compartible (en lugar de solicitudes HTTP directas), puedes construir un conector personalizado de MuleSoft usando el Mule SDK. Esto empaqueta el API de Certyo como un componente Mule de primera clase con operaciones tipadas y autocompletado en Anypoint Studio.
Descripción del conector
- SDK: Mule SDK (basado en Java), disponible a través de la dependencia Maven
org.mule.sdk:mule-sdk-api - Operaciones:
ingestRecord,bulkIngest,verifyRecord,queryRecords - Connection Provider: Gestiona el encabezado
X-API-Keyy la configuración de URL base. Soporta las estrategias de prueba de conexión y reconexión de Mule. - Distribución: Publica en Anypoint Exchange como un asset personalizado, haciéndolo disponible para todos los desarrolladores de tu organización.
# Generate the connector skeleton using the Mule SDK archetype
mvn archetype:generate \
-DarchetypeGroupId=org.mule.extensions \
-DarchetypeArtifactId=mule-extensions-archetype \
-DarchetypeVersion=1.4.0 \
-DgroupId=com.certyos \
-DartifactId=certyo-mule-connector \
-Dversion=1.0.0-SNAPSHOT \
-Dpackage=com.certyos.mule.connector
# Key files to implement:
# src/main/java/.../CertyoConnection.java — API key + base URL
# src/main/java/.../CertyoConnectionProvider.java — connection lifecycle
# src/main/java/.../CertyoOperations.java — ingest, verify, query
# src/main/java/.../CertyoExtension.java — @Extension metadata
# Deploy to Anypoint Exchange:
mvn deploy -DaltDeploymentRepository=anypoint-exchange::default::https://maven.anypoint.mulesoft.com/api/v3/organizations/${orgId}/mavenIngesta masiva con Batch Job
Para escenarios de alto volumen (por ejemplo, exportaciones nocturnas de datos ERP), usa el componente Batch Job de MuleSoft para procesar miles de registros eficientemente a través de POST /api/v1/records/bulk.
<flow name="certyo-bulk-ingestion-flow">
<!-- Trigger: scheduled or event-driven -->
<scheduler>
<scheduling-strategy>
<cron expression="0 0 2 * * ?" timeZone="UTC" />
</scheduling-strategy>
</scheduler>
<!-- Fetch records from source system -->
<http:request config-ref="erp-api-config"
method="GET"
path="/api/export/pending-records" />
<!-- Split into batches of 1000 (Certyo bulk limit) -->
<batch:job jobName="certyo-bulk-batch">
<batch:process-records>
<batch:step name="transform-records">
<ee:transform>
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output application/json
---
{
tenantId: p('certyo.tenant.id'),
records: payload map (record) -> {
database: record.sourceSystem default "erp",
collection: record.entityType default "records",
recordId: record.id as String,
recordPayload: record.data,
sourceTimestamp: record.updatedAt
}
}]]></ee:set-payload>
</ee:message>
</ee:transform>
</batch:step>
<batch:step name="send-to-certyo"
acceptExpression="#[sizeOf(payload.records) > 0]">
<http:request config-ref="certyo-api-config"
method="POST"
path="/api/v1/records/bulk" />
<logger level="INFO"
message="Bulk batch sent: #[sizeOf(payload.records)] records" />
</batch:step>
</batch:process-records>
<batch:on-complete>
<logger level="INFO"
message="Batch complete. Processed: #[payload.processedRecords], Failed: #[payload.failedRecords]" />
</batch:on-complete>
</batch:job>
</flow>AI Integration Skill
Download a skill file that enables AI agents to generate working MuleSoft + Certyo integration code for any language or framework.
What's inside
- Autenticación — Anypoint Connected App y Mule Secure Properties para clave API
- Arquitectura — Conectividad API-led — capas System, Process y Experience
- Mapeo de campos — Carga genérica ERP al formato de registro Certyo vía DataWeave
- Ejemplos de código — Transformaciones DataWeave 2.0, XML de flujo Mule 4, Batch Job para ingesta masiva
- Manejo de errores — Políticas de reintento, backoff exponencial y patrones de cola de mensajes muertos
- Guía del conector — Desarrollo de conector personalizado MuleSoft para el marketplace de Exchange
How to use
Claude Code
Place the file in your project's .claude/commands/ directory, then use it as a slash command:
# Download the skill file
mkdir -p .claude/commands
curl -o .claude/commands/certyo-mulesoft.md \
https://www.certyos.com/developers/skills/certyo-mulesoft-skill.md
# Use it in Claude Code
/certyo-mulesoft "Genera un flujo Mule 4 con DataWeave que enrute eventos ERP a Certyo"Cursor / Copilot / Any AI Agent
Add the file to your project root or attach it to a conversation. The AI agent will use the MuleSoft-specific patterns, field mappings, and code examples to generate correct integration code.
# Add to your project
curl -o CERTYO_MULESOFT.md \
https://www.certyos.com/developers/skills/certyo-mulesoft-skill.md
# Then in your AI agent:
"Using the Certyo MuleSoft spec in CERTYO_MULESOFT.md,
genera un flujo mule 4 con dataweave que enrute eventos erp a certyo"CLAUDE.md Context File
Append the skill file to your project's CLAUDE.md so every Claude conversation has MuleSoft + Certyo context automatically.
# Append to your project's CLAUDE.md
echo "" >> CLAUDE.md
echo "## Certyo MuleSoft Integration" >> CLAUDE.md
cat CERTYO_MULESOFT.md >> CLAUDE.md