Triggo Documentation
SDK

Full Example

End-to-end walkthrough building a complete connector from scratch — a weather service with SecretText auth, one action, and one webhook trigger.

Full Example: Weather Alerts Connector

This walkthrough builds a complete connector for a fictional Weather Alerts API. The connector demonstrates:

  • SecretText authentication with validation
  • An action with multiple property types including a Dropdown
  • A webhook trigger with full lifecycle (onEnable, run, onDisable)

Project Structure

packages/connectors/weather-alerts/
├── src/
│   ├── index.ts
│   ├── weather-alerts.connector.ts
│   ├── actions/
│   │   └── get-forecast.ts
│   └── triggers/
│       └── severe-alert.ts
└── package.json

Step 1: Authentication

Define SecretText auth with a validation call to verify the API key:

// src/weather-alerts.connector.ts
import {
  createConnector,
  ConnectorAuth,
  ConnectorError,
} from "@triggo/connector-sdk";
import { CONNECTOR_ERROR_CODES } from "@triggo/shared";
import { getForecast } from "./actions/get-forecast.ts";
import { severeAlert } from "./triggers/severe-alert.ts";

const auth = ConnectorAuth.SecretText({
  displayName: "API Key",
  description: "Your Weather Alerts API key from the developer dashboard.",
  validate: async (authValue) => {
    const response = await fetch("https://api.weather-alerts.example.com/v1/me", {
      headers: { "X-Api-Key": authValue.secret },
    });

    if (response.status === 401) {
      throw new ConnectorError(
        CONNECTOR_ERROR_CODES.AUTH_EXPIRED,
        "Invalid API key",
      );
    }

    if (!response.ok) {
      throw new ConnectorError(
        CONNECTOR_ERROR_CODES.UPSTREAM_ERROR,
        `Validation failed: ${response.status}`,
      );
    }
  },
});

export const weatherAlertsConnector = createConnector({
  name: "weather-alerts",
  displayName: "Weather Alerts",
  description: "Get weather forecasts and receive severe weather alerts.",
  auth,
  actions: [getForecast],
  triggers: [severeAlert],
});

Step 2: Action — Get Forecast

Create an action that fetches a weather forecast for a given city:

// src/actions/get-forecast.ts
import { createAction, Property, ConnectorError } from "@triggo/connector-sdk";
import { CONNECTOR_ERROR_CODES } from "@triggo/shared";

interface WeatherAuth {
  readonly secret: string;
}

interface ForecastProps {
  readonly city: string;
  readonly units: string;
  readonly days: number;
}

export const getForecast = createAction<WeatherAuth, ForecastProps>({
  name: "get_forecast",
  displayName: "Get Forecast",
  description: "Retrieves a weather forecast for a city.",
  props: {
    city: Property.ShortText({
      displayName: "City",
      description: "City name (e.g., Moscow, Saint Petersburg).",
      required: true,
    }),
    units: Property.Dropdown({
      displayName: "Units",
      description: "Temperature units.",
      required: true,
      defaultValue: "metric",
      options: [
        { label: "Celsius", value: "metric" },
        { label: "Fahrenheit", value: "imperial" },
      ],
    }),
    days: Property.Number({
      displayName: "Forecast Days",
      description: "Number of days to forecast (1-7).",
      required: false,
      defaultValue: 3,
    }),
  },
  errorHandlingOptions: {
    retryOnFailure: { maxRetries: 2, baseIntervalMs: 1000 },
  },
  aiHints:
    "Use this action to get a weather forecast for any city. " +
    "Default is 3 days in Celsius. Supports up to 7 days.",

  async run(context) {
    const { city, units, days } = context.propsValue;
    const apiKey = context.auth.secret;

    const url = new URL("https://api.weather-alerts.example.com/v1/forecast");
    url.searchParams.set("city", city);
    url.searchParams.set("units", units);
    url.searchParams.set("days", String(days));

    const response = await fetch(url.toString(), {
      headers: { "X-Api-Key": apiKey },
    });

    if (response.status === 404) {
      throw new ConnectorError(
        CONNECTOR_ERROR_CODES.NOT_FOUND,
        `City not found: "${city}"`,
      );
    }

    if (response.status === 429) {
      throw new ConnectorError(
        CONNECTOR_ERROR_CODES.RATE_LIMITED,
        "Weather API rate limit exceeded",
      );
    }

    if (!response.ok) {
      throw new ConnectorError(
        CONNECTOR_ERROR_CODES.UPSTREAM_ERROR,
        `Weather API error: ${response.status}`,
      );
    }

    return response.json();
  },
});

Step 3: Trigger — Severe Weather Alert

Create a webhook trigger that fires when a severe weather alert is issued:

// src/triggers/severe-alert.ts
import {
  createTrigger,
  TRIGGER_TYPES,
  ConnectorError,
} from "@triggo/connector-sdk";
import { CONNECTOR_ERROR_CODES } from "@triggo/shared";

interface WeatherAuth {
  readonly secret: string;
}

export const severeAlert = createTrigger<WeatherAuth, Record<string, never>>({
  name: "severe_alert",
  displayName: "Severe Weather Alert",
  description: "Triggers when a severe weather alert is issued for any monitored region.",
  props: {},
  type: TRIGGER_TYPES.WEBHOOK,

  outputSchema: {
    type: "object",
    properties: {
      alertId: { type: "string" },
      severity: { type: "string", enum: ["warning", "watch", "emergency"] },
      region: { type: "string" },
      headline: { type: "string" },
      description: { type: "string" },
      issuedAt: { type: "string", format: "date-time" },
    },
    required: ["alertId", "severity", "region", "headline", "issuedAt"],
  },

  sampleData: {
    alertId: "ALERT-2026-001",
    severity: "warning",
    region: "Moscow Oblast",
    headline: "Heavy Snowfall Warning",
    description: "Expected 20-30cm of snow in the next 24 hours.",
    issuedAt: "2026-02-27T08:00:00.000Z",
  },

  aiHints:
    "Use this trigger to react to severe weather alerts. " +
    "Fires automatically when the weather service detects dangerous conditions.",

  async test() {
    return [
      {
        alertId: "ALERT-TEST-001",
        severity: "warning",
        region: "Test Region",
        headline: "Test Alert",
        description: "This is a test alert.",
        issuedAt: new Date().toISOString(),
      },
    ];
  },

  async onEnable(context) {
    const response = await fetch(
      "https://api.weather-alerts.example.com/v1/webhooks",
      {
        method: "POST",
        headers: {
          "X-Api-Key": context.auth.secret,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          url: context.webhookUrl,
          events: ["alert.severe"],
        }),
      },
    );

    if (!response.ok) {
      throw new ConnectorError(
        CONNECTOR_ERROR_CODES.UPSTREAM_ERROR,
        `Failed to register webhook: ${response.status}`,
      );
    }

    const body = (await response.json()) as { id: string };
    await context.store.put("webhookId", body.id);
    return { externalWebhookId: body.id };
  },

  async onDisable(context) {
    const webhookId = (await context.store.get("webhookId")) as string | null;
    if (webhookId) {
      await fetch(
        `https://api.weather-alerts.example.com/v1/webhooks/${webhookId}`,
        {
          method: "DELETE",
          headers: { "X-Api-Key": context.auth.secret },
        },
      );
      await context.store.delete("webhookId");
    }
  },

  async run(context) {
    const payload = context.payload as Record<string, unknown>;
    return [
      {
        alertId: payload["id"],
        severity: payload["severity"],
        region: payload["region"],
        headline: payload["headline"],
        description: payload["description"],
        issuedAt: payload["issued_at"],
      },
    ];
  },
});

Step 4: Entry Point

Export the connector from the package entry point:

// src/index.ts
export { weatherAlertsConnector } from "./weather-alerts.connector.ts";

What the SDK Does for You

When you call createConnector(), the SDK:

  1. Validates all names (kebab-case for connector, snake_case for actions/triggers)
  2. Generates JSON Schema from property definitions (stored in definition.schema)
  3. Builds lookup maps (actionsMap, triggersMap) for O(1) access by the execution engine
  4. Deep freezes the entire definition to prevent accidental mutation at runtime

The resulting ConnectorDefinition is ready to be registered with the Triggo platform and discovered by the AI pipeline generator.

On this page