Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/manusapis/Agix/llms.txt

Use this file to discover all available pages before exploring further.

Agix pulls live data from external services — Stripe, Salesforce, HubSpot, Google Analytics, Notion, Airtable, and SQL — through a unified connector layer. Every connector implements the same IConnector interface and is registered in a central map in connector.factory.ts. Adding a new data source follows the same three-step pattern for every service you want to support.

The IConnector contract

The interface is defined in src/services/connectors/base.connector.ts:
export interface IConnector {
  readonly type: string;
  connect(config: ConnectorConfig): Promise<boolean>;
  disconnect(): Promise<void>;
  isConnected(): boolean;
  sync(request: SyncRequest): Promise<SyncResult>;
}
BaseConnector provides a concrete isConnected() — it returns true when this.config is not null — and stores the ConnectorConfig in a protected config field. Concrete classes inherit both and only need to implement connect(), disconnect(), and sync().
export abstract class BaseConnector implements IConnector {
  protected config: ConnectorConfig | null = null;

  constructor(public readonly type: string) {}

  abstract connect(config: ConnectorConfig): Promise<boolean>;
  abstract disconnect(): Promise<void>;

  isConnected(): boolean {
    return this.config !== null;
  }

  abstract sync(request: SyncRequest): Promise<SyncResult>;
}
The connector registry in connector.factory.ts is a plain Map. Adding a new entry to that map is all that is needed to make the connector available throughout the application. No other files need to be modified — the getSupportedConnectors() function reads directly from the map’s keys, and the UI enumerates that list dynamically.

Steps

1
Add your type to ConnectorType
2
Open src/types/connector.types.ts and append your connector’s identifier to the ConnectorType union. This string is used as the map key in the registry and as the type field in every ConnectorConfig, SyncRequest, and SyncResult related to your service.
3
// src/types/connector.types.ts
export type ConnectorType =
  | "stripe"
  | "salesforce"
  | "hubspot"
  | "google-analytics"
  | "notion"
  | "airtable"
  | "sql"
  | "shopify";   // ← add your new type here
4
Create a class extending BaseConnector
5
Create a new file at src/services/connectors/shopify.connector.ts. Pass your type string to super(), store the config in connect(), clear it in disconnect(), and implement sync() to fetch the requested resource from the external API.
6
// src/services/connectors/shopify.connector.ts
import { BaseConnector } from "./base.connector";
import type { ConnectorConfig, SyncRequest, SyncResult } from "@/types/connector.types";

export class ShopifyConnector extends BaseConnector {
  constructor() {
    super("shopify");
  }

  async connect(config: ConnectorConfig): Promise<boolean> {
    this.config = config;
    return true;
  }

  async disconnect(): Promise<void> {
    this.config = null;
  }

  async sync(request: SyncRequest): Promise<SyncResult> {
    const res = await fetch(
      `https://${this.config?.credentials.shop}.myshopify.com/admin/api/2024-01/${request.resource}.json`,
      {
        headers: {
          "X-Shopify-Access-Token": this.config?.credentials.accessToken ?? "",
        },
      }
    );

    if (!res.ok) {
      throw new Error(`Shopify sync failed: ${res.status} ${await res.text()}`);
    }

    const data = await res.json();
    return {
      connector: "shopify",
      resource: request.resource,
      syncedAt: new Date().toISOString(),
      rowsInserted: data[request.resource]?.length ?? 0,
      rowsUpdated: 0,
    };
  }
}
7
The SyncResult shape — connector, resource, syncedAt, rowsInserted, and rowsUpdated — is required. The connector factory and the data service both destructure these fields when writing rows back into the spreadsheet.
8
Register in connector.factory.ts
9
Open src/services/connectors/connector.factory.ts, import your class, and add one entry to the registry map:
10
// src/services/connectors/connector.factory.ts
import { ShopifyConnector } from "./shopify.connector";

const registry = new Map<ConnectorType, () => IConnector>([
  ["stripe",           () => new StripeConnector()],
  ["salesforce",       () => new SalesforceConnector()],
  ["hubspot",          () => new HubSpotConnector()],
  ["google-analytics", () => new GoogleAnalyticsConnector()],
  ["notion",           () => new NotionConnector()],
  ["airtable",         () => new AirtableConnector()],
  ["sql",              () => new SQLConnector()],
  ["shopify",          () => new ShopifyConnector()],  // ← new entry
]);
11
Each value is a factory function rather than a singleton so that every call to createConnector() produces a fresh instance with its own isolated config state.

The existing StripeConnector as a reference

The Stripe connector is the simplest shipped implementation and a good template:
// src/services/connectors/stripe.connector.ts
export class StripeConnector extends BaseConnector {
  constructor() {
    super("stripe");
  }

  async connect(config: ConnectorConfig): Promise<boolean> {
    this.config = config;
    return true;
  }

  async disconnect(): Promise<void> {
    this.config = null;
  }

  async sync(request: SyncRequest): Promise<SyncResult> {
    return {
      connector: "stripe",
      resource: request.resource,
      syncedAt: new Date().toISOString(),
      rowsInserted: 0,
      rowsUpdated: 0,
    };
  }
}
Beyond the mandatory IConnector methods, StripeConnector also exposes domain-specific public methods (pullSubscriptions(), pullPaymentHistory(), pullCustomers()) that the data service calls directly. You can do the same for any resource-specific operations that do not fit naturally into the generic sync() signature.

Connector config and credentials

When the user authenticates a connector in the sidebar, the UI assembles a ConnectorConfig and passes it to connector.connect(config). The shape of credentials is a free-form Record<string, string>, so each connector declares its own expected keys by convention:
// ConnectorConfig is defined in src/types/connector.types.ts
export interface ConnectorConfig {
  type: ConnectorType;
  name: string;
  credentials: Record<string, string>;  // e.g. { accessToken: "shpat_…", shop: "my-store" }
  baseUrl?: string;
  rateLimitRps?: number;
  enabled: boolean;
}
Document the expected credential keys in a JSDoc comment on your connector class so future contributors know what the UI must supply.

Files changed summary

FileChange
src/types/connector.types.tsAdd the new ID to the ConnectorType union
src/services/connectors/<name>.connector.tsNew file — extends BaseConnector
src/services/connectors/connector.factory.tsOne new entry in the registry map

Build docs developers (and LLMs) love