Maton
Guide

TypeScript

The official TypeScript / JavaScript SDK, @maton/sdk, wraps the Maton API with typed accessors and automatic retries.

Installation

npm install @maton/sdk
pnpm add @maton/sdk
yarn add @maton/sdk

Quickstart

import { Maton } from "@maton/sdk";

const maton = new Maton({ apiKey: process.env.MATON_API_KEY });

const gmail = maton.google_mail();
const messages = await gmail.message.list({ q: "is:unread", maxResults: 10 });
await gmail.message.send({
  to: "alice@example.com",
  subject: "hi",
  body: "hello",
});

Client

const maton = new Maton({
  apiKey: process.env.MATON_API_KEY, // defaults to MATON_API_KEY env var
  connection: undefined,             // default connection id for every call
  timeoutMs: 30_000,                 // per-request timeout, ms
  maxRetries: 2,                     // retry attempts on transient failures
  maxBackoffMs: 20_000,              // cap on a single backoff sleep, ms
});

App Accessors

Every supported app is available as an accessor with resource-oriented methods:

const slack = maton.slack({ connection: slackConnId });
await slack.message.send({ channel: "#general", text: "deploy finished ✅" });

const gh = maton.github({ connection: ghConnId });
await gh.issue.create({ repo: "maton-ai/maton-ts", title: "bug: ...", body: "..." });

const hubspot = maton.hubspot({ connection: hubspotConnId });
await hubspot.contact.list({ limit: 25 });

Selecting a connection

A connection can be set in three places. Precedence is per-call → accessor → constructor:

const maton = new Maton({ connection: connectionId }); // 1. client default

const gmail = maton.google_mail({ connection: connectionId }); // 2. accessor
await gmail.message.list({ q: "is:unread" });

await maton.google_mail.message.list({                 // 3. per-call (wins)
  q: "is:unread",
  connection: connectionId,
});

Connections

await maton.connection.create({ app: "slack" });                   // PENDING connection + auth url
await maton.connection.get({ connectionId });
await maton.connection.list({ app: "slack", status: "ACTIVE" });
await maton.connection.delete({ connectionId });

create returns the connection with a url field — open it in a browser to complete OAuth, then poll get until status is "ACTIVE".

Passthrough

For any endpoint without a typed accessor, use maton.api to reach it through the gateway:

await maton.api.get("slack", "/api/conversations.list", {
  query: { limit: 10 },
  connection: connectionId,
});

await maton.api.post("google-mail", "/gmail/v1/users/me/messages/send", {
  json: { raw: "..." },
  connection: connectionId,
});

Triggers

Triggers register an event source (e.g. GitHub pull_request.opened) and fan matching events out to webhook destinations. Manage them through maton.trigger, with maton.trigger.destination and maton.trigger.event sub-resources:

const trigger = (await maton.trigger.create({
  source: "github",
  eventType: "pull_request.opened",
  connectionId: ghConnId,
  parameters: { repository_full_name: "maton-ai/cli" },
  destinations: [{ url: "https://example.com/hook" }],
})) as { trigger: { trigger_id: string } };
const triggerId = trigger.trigger.trigger_id;

await maton.trigger.list({ source: "github", status: "ENABLED" });
await maton.trigger.get({ triggerId });
await maton.trigger.update({ triggerId, status: "DISABLED" });
await maton.trigger.delete({ triggerId });

// Destinations
const dst = (await maton.trigger.destination.create({
  triggerId,
  url: "https://example.com/hook",
})) as { destination: { destination_id: string } };
await maton.trigger.destination.rotateSecret({
  triggerId,
  destinationId: dst.destination.destination_id,
});

// Events
const events = (await maton.trigger.event.list({ triggerId, limit: 20 })) as {
  events: { event_id: string }[];
};
await maton.trigger.event.replay({ triggerId, eventId: events.events[0].event_id });

Watching events

watch is an async iterator that yields each new event as it arrives:

for await (const event of maton.trigger.event.watch({ triggerId })) {
  handle(event);
}

Reliability

Every call goes through a fetch-based client with automatic retries. Retries fire on connection errors and on 429 / 500 / 502 / 503 / 504; other 4xx/5xx surface immediately. Backoff is truncated exponential with full jitter and honors a server-provided Retry-After header.

Errors

All failures throw a subclass of MatonError, each carrying statusCode, requestId, and the parsed body:

ErrorHTTP status
AccessDeniedError401, 403 — bad/missing API key or insufficient scope
ResourceNotFoundError404
TooManyRequestsError (carries retryAfter)429
ValidationErrorother 4xx (incl. 412 when an action needs a connection)
InternalServerError5xx or unexpected upstream response
APIConnectionErrortransport failure / timeout
import { Maton, MatonError, TooManyRequestsError } from "@maton/sdk";

try {
  await maton.google_mail.message.get({ messageId, connection: connectionId });
} catch (err) {
  if (err instanceof TooManyRequestsError) {
    console.error("slow down; retry after", err.retryAfter);
  } else if (err instanceof MatonError) {
    console.error(err.statusCode, err.requestId, err.body);
  }
  throw err;
}

Some apps wrap their vendor-specific error envelopes in a dedicated subclass (GitHubError, GoogleDriveError, LinearError, SlackError, StripeError, YouTubeError) so you can catch them by app while still falling back to MatonError.

On this page