Crear una integración de canal
Esta es una referencia para desarrolladores para cualquiera que extienda Structura con un nuevo canal. Si buscas cómo usar los canales existentes, mira Cómo funcionan los canales en su lugar.
Esta guía recorre el añadido de una nueva integración al sistema de canales. Usaremos una integración hipotética de «Mailchimp» como ejemplo.
Paso 1: Elegir capacidades
Decide qué interfaces de capacidad necesita tu integración:
| Si tu integración… | Implementa |
|---|---|
| Envía notificaciones fire-and-forget | NotifiableIntegration |
| Reescribe contenido vía IA para la plataforma | AdaptableIntegration |
| Publica contenido a la API de la plataforma | PublishableIntegration |
| Usa OAuth 2.0 para autenticarse | OAuthIntegration |
| Se configura pegando una URL | WebhookIntegration |
Las capacidades son combinables. LinkedIn implementa OAuth + Adapt + Publish. Slack implementa Webhook + Notify. IndexNow implementa solo Notify.
Paso 2: Crear la clase Integration
Crea
functions/src/channels/integrations/MailchimpIntegration.ts:
import type { FetchLike } from "../contracts/Integration.js";
import type {
Integration,
IntegrationMetadata,
HealthStatus,
ConnectionRecord,
} from "../contracts/Integration.js";
import type { NotifiableIntegration, NotifyContext, NotifyResult } from "../contracts/Integration.js";
export const MAILCHIMP_INTEGRATION_ID = "mailchimp";
export class MailchimpIntegration implements Integration, NotifiableIntegration {
constructor(private readonly fetch: FetchLike = globalThis.fetch) {}
readonly metadata: IntegrationMetadata = {
id: MAILCHIMP_INTEGRATION_ID,
name: "Mailchimp",
category: "email",
sku: "channels", // or "free" for free integrations
capabilities: ["notify"],
authType: "apikey", // or "oauth2", "webhook", "none"
iconUrl: "/icons/mailchimp.svg",
};
async healthCheck(connection: ConnectionRecord): Promise<HealthStatus> {
if (connection.status === "connected") return { status: "healthy" };
return { status: "unhealthy", reason: connection.lastError?.message };
}
async notify(ctx: NotifyContext): Promise<NotifyResult> {
// Implementation here
}
}Reglas clave:
- Acepta
FetchLikepor el constructor para testabilidad. - Nunca codifiques llamadas HTTP — usa siempre el
fetchinyectado. - Clasifica los errores HTTP de forma consistente (mira la tabla de clasificación de errores en Contratos de integración de canal).
Paso 3: Registrar la integración
Añade tu clase a
functions/src/channels/registry/IntegrationRegistry.ts:
import { MailchimpIntegration } from "../integrations/MailchimpIntegration.js";
const CATALOG: Integration[] = [
// ... existing integrations
new MailchimpIntegration(),
];Paso 4: Añadir una entrada de catálogo
Añade una entrada a
functions/src/channels/registry/catalog.ts:
{
id: "mailchimp",
name: "Mailchimp",
description: "Send email campaigns when posts are published",
category: "email",
capabilities: ["notify"],
authType: "apikey",
gating: {
requiredPlan: "free", // Minimum plan
requiredAddon: "channels", // null if free, "channels" if paid
},
iconUrl: "/icons/mailchimp.svg",
}Paso 5: Escribir pruebas
Crea
functions/src/channels/__tests__/MailchimpIntegration.test.ts:
import { describe, it, expect, vi } from "vitest";
import { MailchimpIntegration } from "../integrations/MailchimpIntegration.js";
describe("MailchimpIntegration", () => {
it("has correct metadata", () => {
const integration = new MailchimpIntegration();
expect(integration.metadata.id).toBe("mailchimp");
expect(integration.metadata.capabilities).toContain("notify");
});
it("sends notification on publish", async () => {
const stubFetch = vi.fn().mockResolvedValue({
ok: true, status: 200, json: async () => ({}),
});
const integration = new MailchimpIntegration(stubFetch);
const result = await integration.notify(/* NotifyContext */);
expect(result.status).toBe("ok");
expect(stubFetch).toHaveBeenCalledOnce();
});
// Test error classification for each HTTP status...
});Actualiza IntegrationRegistry.test.ts para incluir la
nueva integración en los conteos esperados.
Paso 6: Actualizar el plugin de WordPress (si hace falta)
Para integraciones basadas en credenciales o webhook, no
hacen falta cambios en el plugin — los endpoints
existentes channelsSaveCredentialConnection y
channelsSaveWebhookConnection manejan todos los tipos
de auth de forma genérica.
Para integraciones OAuth, los endpoints existentes
channelsOAuthInit y channelsOAuthCallback manejan el
flujo. Puede que necesites añadir nuevas entradas
defineSecret() para el client ID/secret del proveedor.
Paso 7: Añadir documentación
Crea docs/pages/channels/integrations/mailchimp.mdx con:
- Cómo funciona la integración
- Tabla de metadatos
- Forma del secreto descifrado
- Instrucciones de configuración de la conexión
- Tabla de manejo de errores
Actualiza docs/pages/channels/integrations/_meta.json
para incluir la nueva página.
Lista de comprobación
- Clase Integration que implementa las interfaces de capacidad correctas
- Inyección de dependencia
FetchLike - Clasificación de errores para todos los códigos de estado HTTP
- Mensajes con conciencia de idioma (si NotifiableIntegration)
- Implementación de health check
- Registrada en
IntegrationRegistry - Entrada de catálogo con gating correcto
- Pruebas unitarias (metadatos, happy path, casos de error, casos límite)
- Prueba de Registry actualizada con los nuevos conteos esperados
- Página de documentación
- Secretos configurados (si OAuth o clave API)