Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.useterse.ai/llms.txt

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

Triggers define the event that starts your workflow. Each trigger is tied to an integration and fires when a specific event occurs. Your onTrigger handler receives a typed event object with the payload. In TypeScript, terse generate writes a single Triggers object in src/terse.generated.ts. Import it and call the nested helper for your event (for example Triggers.github.onPROpened(...)). Each integration and system trigger has its own section below.

Integration triggers

GitHub

TriggerEvent typeDescription
Triggers.github.onPush()GithubPushTriggerCode is pushed to a branch
Triggers.github.onPROpened()GithubPROpenedTriggerA pull request is opened
Triggers.github.onPRMerged()GithubPRMergedTriggerA pull request is merged
Triggers.github.onPRClosed()GithubPRClosedTriggerA pull request is closed
Triggers.github.onPRSynchronized()GithubPRSynchronizedTriggerNew commits are pushed to a PR
Triggers.github.onPR()GithubPRTriggerAny pull request event
Triggers.github.onIssueComment()GithubIssueCommentCreatedTriggerA comment is created on an issue or pull request
Triggers.github.trigger()GithubTriggerAny GitHub event you select with eventTypes
Config: Scoped to specific repositories via repo parameter.
triggers: [Triggers.github.onPROpened({ repo: Repos.MyOrg.MyRepo })]

Slack

TriggerEvent typeDescription
Triggers.slack.onMessage()SlackMessageTriggerA message is posted in a channel
Triggers.slack.onDm()SlackMessageTriggerA direct message is received
Triggers.slack.onAppMention()SlackAppMentionTriggerThe Slack app is mentioned in a channel
Triggers.slack.onReactionAdded()SlackReactionAddedTriggerAn emoji reaction is added to a message
Triggers.slack.trigger()SlackTrigger (union)Any Slack event you select with eventTypes
Config: Scoped to specific channels or users.
triggers: [Triggers.slack.onMessage({ channel: SlackChannel.Engineering })]

Linear

TriggerEvent typeDescription
Triggers.linear.onIssueCreated()LinearIssueCreatedTriggerAn issue is created
Triggers.linear.onIssueUpdated()LinearIssueUpdatedTriggerAn issue is updated
Triggers.linear.onComment()LinearCommentCreatedTriggerA comment is added to an issue
Triggers.linear.trigger()LinearTrigger (union)Any Linear event you select with eventTypes
Config: Optionally scoped to a team and/or project using generated LinearTeam / LinearProject constants (same pattern as SlackChannel for Slack).
triggers: [Triggers.linear.onIssueCreated({ team: LinearTeam.Engineering, project: LinearProject.Backend })]

Attio

Connect Attio under Integrations, configure which Attio events start each workflow in the Terse app, then run terse generate. When Attio input triggers are enabled for your workspace, Triggers.attio appears in src/terse.generated.ts.
TriggerEvent typeDescription
Triggers.attio.onRecordCreated()AttioRecordCreatedTriggerA record is created
Triggers.attio.onRecordUpdated()AttioRecordUpdatedTriggerA record is updated
Triggers.attio.onRecordMerged()AttioRecordMergedTriggerTwo records are merged
Triggers.attio.onRecordDeleted()AttioRecordDeletedTriggerA record is deleted
Triggers.attio.onObjectAttributeCreated()AttioObjectAttributeCreatedTriggerAn object attribute is created
Triggers.attio.onObjectAttributeUpdated()AttioObjectAttributeUpdatedTriggerAn object attribute is updated
Triggers.attio.onListCreated()AttioListCreatedTriggerA list is created
Triggers.attio.onListUpdated()AttioListUpdatedTriggerA list is updated
Triggers.attio.onListDeleted()AttioListDeletedTriggerA list is deleted
Triggers.attio.onListAttributeCreated()AttioListAttributeCreatedTriggerA list attribute is created
Triggers.attio.onListAttributeUpdated()AttioListAttributeUpdatedTriggerA list attribute is updated
Triggers.attio.onListEntryCreated()AttioListEntryCreatedTriggerA list entry is created
Triggers.attio.onListEntryUpdated()AttioListEntryUpdatedTriggerA list entry is updated
Triggers.attio.onListEntryDeleted()AttioListEntryDeletedTriggerA list entry is deleted
Triggers.attio.onNoteCreated()AttioNoteCreatedTriggerA note is created
Triggers.attio.onNoteContentUpdated()AttioNoteContentUpdatedTriggerA note’s content is updated
Triggers.attio.onNoteUpdated()AttioNoteUpdatedTriggerA note is updated
Triggers.attio.onNoteDeleted()AttioNoteDeletedTriggerA note is deleted
Triggers.attio.onCommentCreated()AttioCommentCreatedTriggerA comment is created
Triggers.attio.onCommentResolved()AttioCommentResolvedTriggerA comment is resolved
Triggers.attio.onCommentUnresolved()AttioCommentUnresolvedTriggerA resolved comment is reopened
Triggers.attio.onCommentDeleted()AttioCommentDeletedTriggerA comment is deleted
Triggers.attio.onTaskCreated()AttioTaskCreatedTriggerA task is created
Triggers.attio.onTaskUpdated()AttioTaskUpdatedTriggerA task is updated
Triggers.attio.onTaskDeleted()AttioTaskDeletedTriggerA task is deleted
Triggers.attio.onWorkspaceMemberCreated()AttioWorkspaceMemberCreatedTriggerA workspace member is added
Triggers.attio.onCallRecordingCreated()AttioCallRecordingCreatedTriggerA call recording is created
Triggers.attio.trigger()AttioTrigger (union)Any Attio events you select with eventTypes
Config: Pass object: AttioObject.YourObject on the record helpers (onRecordCreated, onRecordUpdated, onRecordMerged, onRecordDeleted) to scope deliveries to a single CRM object. Omit object when you want the union of every generated object’s attribute shape.
import { Triggers } from "./terse.generated"

triggers: [Triggers.attio.onRecordCreated({ object: AttioObject.People })]

Gmail

TriggerEvent typeDescription
Triggers.gmail.onEmail()GmailTriggerA new email is received
Triggers.gmail.trigger()GmailTrigger (union)Any Gmail event you select with eventTypes
triggers: [Triggers.gmail.onEmail()]

HeyReach

HeyReach fires when LinkedIn outreach events occur (messages, connection requests, campaign milestones, and more). Connect HeyReach under Integrations in the Terse app, then run terse generate so Triggers.heyReach and typed HeyReachCampaign constants appear in src/terse.generated.ts. Each helper maps to one HeyReachEventType value (also exported from terse-sdk). Use Triggers.heyReach.trigger({ eventType: HeyReachEventType.MESSAGE_REPLY_RECEIVED }) when you want to pick the event type dynamically.
TriggerEvent type
Triggers.heyReach.onConnectionRequestSent()HeyReachConnectionRequestSentTrigger
Triggers.heyReach.onConnectionRequestAccepted()HeyReachConnectionRequestAcceptedTrigger
Triggers.heyReach.onMessageSent()HeyReachMessageSentTrigger
Triggers.heyReach.onMessageReplyReceived()HeyReachMessageReplyReceivedTrigger
Triggers.heyReach.onInmailSent()HeyReachInmailSentTrigger
Triggers.heyReach.onInmailReplyReceived()HeyReachInmailReplyReceivedTrigger
Triggers.heyReach.onFollowSent()HeyReachFollowSentTrigger
Triggers.heyReach.onLikedPost()HeyReachLikedPostTrigger
Triggers.heyReach.onViewedProfile()HeyReachViewedProfileTrigger
Triggers.heyReach.onCampaignCompleted()HeyReachCampaignCompletedTrigger
Triggers.heyReach.onLeadTagUpdated()HeyReachLeadTagUpdatedTrigger
Config: Pass campaigns with generated HeyReachCampaign instances to handle only those campaigns; omit it to receive events from all campaigns.
import { Triggers } from "./terse.generated"

triggers: [Triggers.heyReach.onMessageReplyReceived()]

WorkOS

TriggerEvent typeDescription
Triggers.workOS.onUserCreated()WorkOSUserCreatedTriggerA user is created
Triggers.workOS.onUserUpdated()WorkOSUserUpdatedTriggerA user is updated
Triggers.workOS.onUserDeleted()WorkOSUserDeletedTriggerA user is deleted
Triggers.workOS.onMembershipCreated()WorkOSOrganizationMembershipCreatedTriggerAn organization membership is created
Triggers.workOS.onMembershipUpdated()WorkOSOrganizationMembershipUpdatedTriggerAn organization membership is updated
Triggers.workOS.onMembershipDeleted()WorkOSOrganizationMembershipDeletedTriggerAn organization membership is deleted
Triggers.workOS.onMembershipChanged()WorkOSMembershipTriggerAny membership change
Triggers.workOS.onInvitationCreated()WorkOSInvitationCreatedTriggerAn invitation is created
Triggers.workOS.onInvitationResent()WorkOSInvitationResentTriggerAn invitation is resent
Triggers.workOS.onInvitationSent()WorkOSInvitationTriggerAn invitation is created or resent
Triggers.workOS.onInvitationAccepted()WorkOSInvitationAcceptedTriggerAn invitation is accepted
Triggers.workOS.onInvitationRevoked()WorkOSInvitationRevokedTriggerAn invitation is revoked
Triggers.workOS.onOrganizationCreated()WorkOSOrganizationTriggerAn organization is created
Triggers.workOS.trigger()WorkOSTrigger (union)Any WorkOS event you select with eventTypes
Config: Use a typed helper for one event, or pass eventTypes to trigger() for multiple.
import { WorkOSEventType } from "terse-sdk"

triggers: [Triggers.workOS.trigger({ eventTypes: [WorkOSEventType.USER_CREATED] })]

Web Monitor

Track web changes continuously and get notified when there are updates. You can add multiple Triggers.webMonitor.onEvent(...) triggers to the same workflow. Terse runs each monitor independently and calls the same onTrigger handler for every matching event.
TriggerEvent typeDescription
Triggers.webMonitor.onEvent()WebMonitorTriggerFires when scheduled web monitoring detects relevant changes
import { createJob, generateText } from "terse-sdk"
import * as z from "zod"
import { Skills, SlackChannel, Triggers } from "./terse.generated"

const outputSchema = z.object({
    summary: z.string(),
    sentiment: z.enum(["positive", "negative", "neutral"])
})

createJob({
    name: "Web monitor trigger",
    triggers: [
        Triggers.webMonitor.onEvent({
            query: "What are a16z's latest investments?",
            frequency: { number: 1, unit: "day" },
            outputSchema
        })
    ],
    onTrigger: async event => {
        const summary = event.payload.summary
        const sentiment = event.payload.sentiment
        const sources = event.sourceUrls.join(", ")

        await generateText({
            prompt:
                "You are alerted when the web monitor finds new material. Read the event below (query, payload, and structured fields), decide what matters, and post a concise summary to Slack.\n\n" +
                `${event.formatForAgentRunner()}\n\nSummary: ${summary}\nSentiment: ${sentiment}\nSources: ${sources}`,
            skills: [Skills.slack({ channel: SlackChannel.AllTerseInc })]
        })
    }
})
If you need to annotate monitor events manually, import WebMonitorTriggerFor from terse-sdk and bind it to your Zod schema:
import type { WebMonitorTriggerFor } from "terse-sdk";
import * as z from "zod";

const outputSchema = z.object({
  summary: z.string(),
  sentiment: z.enum(["positive", "negative", "neutral"]),
});

type MonitorEvent = WebMonitorTriggerFor<typeof outputSchema>

Writing effective queries

The monitor matches by intent, not keywords. Write your query as a natural sentence describing what you care about — not a search string. Do:
  • Write in plain language: "What are a16z's latest investments?"
  • Track narratives and social buzz: "What are people saying on Twitter about Cloud agents?"
  • Be specific about subject and signal: "FDA approval decisions for GLP-1 drugs"
  • Combine related signals: "OpenAI or Anthropic model releases and benchmark results"
Don’t:
  • Use Boolean operators (AND, OR, NOT) — this is not a search engine
  • Include specific dates — monitors track new developments going forward, not history
  • Write keyword dumps: "acme corp acme funding raise series B"
Choosing frequency:
FrequencyBest for
1hBreaking news, social signals, fast-moving markets
1dPress releases, blog posts, job postings, pricing page changes
1wRegulatory filings, quarterly reports, slow competitive signals

System triggers

Schedule

TriggerEvent typeDescription
Triggers.schedule.cron()CronTriggerFires on a cron schedule
triggers: [Triggers.schedule.cron({ expression: "0 9 * * 1" })] // Every Monday at 9am

Webhook

TriggerEvent typeDescription
Triggers.webhook.onRequest()WebhookTriggerFires when an HTTP request hits the generated webhook URL
Webhook URLs are auto-generated on deploy and remain stable across redeploys. After terse deploy, the CLI prints that URL for each affected workflow so you can copy it straight into your caller or load test. Pass a type argument to Triggers.webhook.onRequest<YourPayload>() so onTrigger and filter see your JSON body shape on event.body (defaults to unknown when omitted).
triggers: [Triggers.webhook.onRequest()]

Common event interface

All triggers resolve to the shared canonical Trigger payload shape. Your handler receives the concrete subtype for the trigger you selected.
interface Trigger {
  readonly integrationType: string
  readonly eventType: string
}
Each trigger subtype adds its own canonical fields, such as Slack message metadata, GitHub PR data, webhook request bodies, or cron trigger context.