> ## Documentation Index
> Fetch the complete documentation index at: https://learn.actionist.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Reactions

> Connect any agent to the outside world — HTTP webhooks, Gmail, Drive, Calendar, and marketplace apps. The agent wakes, acts, and logs every event automatically.

<div className="xre-hero xc-border">
  <div className="exp-eyebrow mxp-rise">EVENT-DRIVEN AGENTS</div>

  <h2 className="mxp-hero-title mxp-rise" style={{animationDelay:'0.06s'}}>
    Your agent picks up the phone <span style={{background:'linear-gradient(90deg,#24A4FF,#9000FF,#E3008E)',WebkitBackgroundClip:'text',backgroundClip:'text',color:'transparent'}}>when the world calls.</span>
  </h2>

  <p className="mxp-hero-dek mxp-rise" style={{animationDelay:'0.13s'}}>
    Wire any agent to an inbound HTTP webhook, a Gmail inbox, a Drive folder, a Google Calendar, or a marketplace app. No polling script, no relay service, no cron job — the agent wakes the instant an event fires and leaves a full audit trail behind.
  </p>
</div>

<div className="w-full py-10">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">The filter layer</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">Watch a payload pass — or get dropped.</h2>

    <p className="text-base text-gray-600 dark:text-gray-400 max-w-3xl">
      Every inbound event runs through your filter rules before the agent wakes. Pick a payload and see exactly what the filter decides.
    </p>
  </div>

  <div className="xre-sim exp-tool-shell xc-try">
    <div className="mxp-srch-chips">
      <label htmlFor="xre-payload-a"><input className="mxp-r" type="radio" name="xre-pl" id="xre-payload-a" defaultChecked />amount: 1200, status: paid</label>
      <label htmlFor="xre-payload-b"><input className="mxp-r" type="radio" name="xre-pl" id="xre-payload-b" />amount: 80, status: refunded</label>
    </div>

    <div className="xre-filter-card" style={{marginTop:'20px'}}>
      <div style={{fontSize:'12px',letterSpacing:'0.07em',textTransform:'uppercase',opacity:0.55,marginBottom:'10px',fontWeight:600}}>Filter rule (All conditions must match)</div>

      <div className="xre-filter-rule">
        <span style={{opacity:0.65}}>payload</span>
        <span style={{opacity:0.9}}><strong>amount</strong></span>
        <span className="xre-fop">greater\_than</span>
        <span style={{opacity:0.9}}>500</span>
        <span className="xre-fand">AND</span>
        <span style={{opacity:0.65}}>payload</span>
        <span style={{opacity:0.9}}><strong>status</strong></span>
        <span className="xre-fop">equals</span>
        <span style={{opacity:0.9}}>"paid"</span>
      </div>

      <div className="xre-sim-panel xre-panel-a">
        <div className="xre-chip-row">
          <span className="xre-chip xre-is-match">amount: 1200</span>
          <span className="xre-chip xre-is-match">status: "paid"</span>
          <span className="xre-chip">customer: "[alex@example.com](mailto:alex@example.com)"</span>
        </div>
      </div>

      <div className="xre-sim-panel xre-panel-b">
        <div className="xre-chip-row">
          <span className="xre-chip xre-is-miss">amount: 80</span>
          <span className="xre-chip xre-is-miss">status: "refunded"</span>
          <span className="xre-chip">customer: "[dana@example.com](mailto:dana@example.com)"</span>
        </div>
      </div>
    </div>

    <div className="xre-outcome xre-outcome-pass">
      <span className="xre-outcome-dot" />

      <strong>PASS — agent fires</strong>
      <span style={{fontWeight:400,opacity:0.75,fontSize:'13px',marginLeft:'4px'}}>Both conditions matched. Instruction rendered. Agent dispatched.</span>
    </div>

    <div className="xre-outcome xre-outcome-drop">
      <span className="xre-outcome-dot" />

      <strong>FILTERED — event dropped</strong>
      <span style={{fontWeight:400,opacity:0.75,fontSize:'13px',marginLeft:'4px'}}>amount 80 is not greater than 500. Agent was not invoked. Status: Filtered.</span>
    </div>

    <div className="mxp-window" style={{marginTop:'24px'}}>
      <div className="mxp-window-bar">
        <span className="mxp-window-dot" />

        <span className="mxp-window-dot" />

        <span className="mxp-window-dot" />

        <span className="mxp-window-title">trigger · stripe-payments · rendered instruction</span>
      </div>

      <div className="mxp-window-body" style={{minHeight:'auto',padding:'14px 18px'}}>
        <div className="xre-sim-panel xre-panel-a">
          <div className="mxp-stream-line"><span className="mxp-stream-time">task</span><span className="mxp-stream-text">Process the new payment for <em>[alex@example.com](mailto:alex@example.com)</em> (amount: <em>\$1200</em>) and log it to the CRM.</span></div>
          <div style={{margin:'8px 0'}}><span className="mxp-stream-event">tool · memory\_search · "[alex@example.com](mailto:alex@example.com)"</span></div>
          <div className="mxp-stream-line"><span className="mxp-stream-time">agent</span><span className="mxp-stream-text">Payment logged under Alex's account. Invoice marked <em>paid</em>. CRM updated.<span className="mxp-caret" /></span></div>
        </div>

        <div className="xre-sim-panel xre-panel-b">
          <div className="mxp-stream-line"><span className="mxp-stream-time">filter</span><span className="mxp-stream-text" style={{opacity:0.5}}>Event evaluated — amount 80 is not greater than 500. Condition failed.</span></div>
          <div style={{margin:'8px 0'}}><span className="mxp-stream-event" style={{opacity:0.5}}>event · status: Filtered · agent not invoked</span></div>
          <div className="mxp-stream-line"><span className="mxp-stream-time">log</span><span className="mxp-stream-text" style={{opacity:0.5}}>Recorded in Recent events. Payload retained for 7 days.</span></div>
        </div>
      </div>
    </div>
  </div>

  <div className="exp-surface-grid" style={{marginTop:'22px'}}>
    <div className="exp-surface-tile">
      <strong>No false wakes.</strong> Filters run before the agent is invoked — a filtered event costs nothing and never starts a run.
    </div>

    <div className="exp-surface-tile">
      <strong>AND or OR logic.</strong> Choose <strong>All conditions</strong> (every rule must match) or <strong>Any condition</strong> (any single rule is enough) to build compound rules.
    </div>

    <div className="exp-surface-tile">
      <strong>Dry Run first.</strong> Paste any payload and confirm the filter decision instantly, client-side, before a single real event arrives.
    </div>
  </div>
</div>

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">Under the hood</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">Five stops from webhook to completed run.</h2>

    <p className="text-base text-gray-600 dark:text-gray-400 max-w-3xl">
      Every inbound event moves through a deterministic lifecycle — watch it pulse through the stages below.
    </p>
  </div>

  <div className="mxp-pipe">
    <div className="mxp-pipe-node n1">
      <strong>Received</strong>
      <span>Event arrived and persisted. Not yet processed.</span>
    </div>

    <div className="mxp-pipe-link" />

    <div className="mxp-pipe-node n2">
      <strong>Ready</strong>
      <span>Parsed and queued. Awaiting worker claim.</span>
    </div>

    <div className="mxp-pipe-link d2" />

    <div className="mxp-pipe-node n3">
      <strong>Claimed</strong>
      <span>Worker picked it up. Agent run starting.</span>
    </div>

    <div className="mxp-pipe-link d3" />

    <div className="mxp-pipe-node n4">
      <strong>Dispatched</strong>
      <span>Agent actively running. Tool calls in flight.</span>
    </div>

    <div className="mxp-pipe-link d4" />

    <div className="mxp-pipe-node n5">
      <strong>Completed</strong>
      <span>Run finished. Result delivered. Transcript linked.</span>
    </div>
  </div>

  <div className="xre-status-row" style={{marginTop:'22px'}}>
    <span className="xre-status s-received"><span className="xre-dot" />Received</span>
    <span className="xre-status s-ready"><span className="xre-dot" />Ready</span>
    <span className="xre-status s-claimed"><span className="xre-dot" />Claimed</span>
    <span className="xre-status s-dispatched"><span className="xre-dot" />Dispatched</span>
    <span className="xre-status s-completed"><span className="xre-dot" />Completed</span>
    <span className="xre-status s-filtered"><span className="xre-dot" />Filtered</span>
    <span className="xre-status s-failed"><span className="xre-dot" />Failed</span>
    <span className="xre-status s-expired"><span className="xre-dot" />Expired</span>
  </div>

  <Note>
    The <strong>\~45-second poll interval</strong> means an event delivered to the backend can take up to 45 seconds before the agent starts processing. Design time-sensitive workflows accordingly.
  </Note>
</div>

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">The concept</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">Stop polling. Start reacting.</h2>

    <p className="text-base text-gray-600 dark:text-gray-400 max-w-3xl">
      A Reaction is the configuration that connects an external event source to an agent. Every time a matching event arrives, Actionist hands the event data to your agent along with a natural-language task — and the agent runs.
    </p>
  </div>

  <Columns cols={2}>
    <Card title="The old way" icon="clock">
      Write a polling script. Deploy a webhook relay. Schedule a cron job. Check your inbox manually. Every approach involves infrastructure you have to build, host, and monitor before your agent sees a single byte of data.
    </Card>

    <Card title="With Reactions" icon="zap">
      Pick a source, write a task description in plain English, and click <strong>Create trigger</strong>. Actionist handles the subscription, receives the event, renders your instruction, runs the agent, and logs the whole thing — while you do something else.
    </Card>
  </Columns>

  <Note>
    Two vocabulary terms used throughout this page: <strong>trigger</strong> is the saved configuration (source + task + filter + delivery settings); <strong>event</strong> is each individual inbound payload that the trigger receives. One trigger receives many events over its lifetime.
  </Note>

  <Info>
    Reactions are a desktop-only feature. Trigger creation, management, and event history require the Actionist desktop app.
  </Info>
</div>

<hr className="xc-divider" />

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">Sources</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">Three ways the world can call your agent.</h2>

    <p className="text-base text-gray-600 dark:text-gray-400 max-w-3xl">
      Pick a source type in the creation drawer — each one sets up differently. Select below to see what each involves.
    </p>
  </div>

  <div className="xre-trip exp-tool-shell xc-try">
    <div className="mxp-srch-chips">
      <label htmlFor="xre-src-a"><input className="mxp-r" type="radio" name="xre-src" id="xre-src-a" defaultChecked />HTTP webhook</label>
      <label htmlFor="xre-src-b"><input className="mxp-r" type="radio" name="xre-src" id="xre-src-b" />Google (Gmail · Drive · Calendar)</label>
      <label htmlFor="xre-src-c"><input className="mxp-r" type="radio" name="xre-src" id="xre-src-c" />App trigger</label>
    </div>

    <div className="xre-src-panel xre-src-panel-a">
      <div className="mxp-window" style={{marginTop:'18px'}}>
        <div className="mxp-window-bar">
          <span className="mxp-window-dot" />

          <span className="mxp-window-dot" />

          <span className="mxp-window-dot" />

          <span className="mxp-window-title">HTTP webhook — setup</span>
        </div>

        <div className="mxp-window-body" style={{minHeight:'auto',padding:'14px 18px'}}>
          <div className="mxp-stream-line mxp-s1"><span className="mxp-stream-time">1.</span><span className="mxp-stream-text">Name the trigger (e.g. <em>New Stripe payment</em>). Select the <em>HTTP webhook</em> tile.</span></div>
          <div className="mxp-stream-line mxp-s2"><span className="mxp-stream-time">2.</span><span className="mxp-stream-text">Choose auth: <em>Public (no auth)</em> or <em>Bearer secret</em> backed by the Credentials Vault.</span></div>
          <div className="mxp-stream-line mxp-s3"><span className="mxp-stream-time">3.</span><span className="mxp-stream-text">Save. Copy the generated webhook URL from the trigger row.</span></div>
          <div className="mxp-stream-line mxp-s4"><span className="mxp-stream-time">4.</span><span className="mxp-stream-text">Paste the URL into Stripe / GitHub / Typeform — or any service that sends webhooks.</span></div>
          <div style={{margin:'10px 0 4px'}}><span className="mxp-stream-event">supports any JSON payload · up to 256 KB · bearer secret via X-Actionist-Trigger-Secret header</span></div>
        </div>
      </div>

      <p style={{fontSize:'13.5px',opacity:0.7,margin:'12px 2px 0'}}>The source type is <strong>locked after save</strong>. If you need to switch from HTTP to Google (or vice versa), delete the trigger and recreate it. The UI shows: <em>"Delete and recreate to change (avoids subscription orphan)."</em></p>
    </div>

    <div className="xre-src-panel xre-src-panel-b">
      <div className="mxp-window" style={{marginTop:'18px'}}>
        <div className="mxp-window-bar">
          <span className="mxp-window-dot" />

          <span className="mxp-window-dot" />

          <span className="mxp-window-dot" />

          <span className="mxp-window-title">Google — 3-step inline wizard</span>
        </div>

        <div className="mxp-window-body" style={{minHeight:'auto',padding:'14px 18px'}}>
          <div className="mxp-stream-line mxp-s1"><span className="mxp-stream-time">step A</span><span className="mxp-stream-text">Choose a service: <em>Gmail</em> · <em>Drive</em> · <em>Calendar</em></span></div>
          <div className="mxp-stream-line mxp-s2"><span className="mxp-stream-time">step B</span><span className="mxp-stream-text">Select an event type (8 types across the three services).</span></div>
          <div className="mxp-stream-line mxp-s3"><span className="mxp-stream-time">step C</span><span className="mxp-stream-text">Pick your Google account, then narrow by label / folder / calendar.</span></div>
          <div style={{margin:'10px 0 4px'}}><span className="mxp-stream-event">native push subscriptions · no middleman · subscription health badge on trigger row</span></div>
        </div>
      </div>

      <p style={{fontSize:'13.5px',opacity:0.7,margin:'12px 2px 0'}}>Gmail: <em>New message</em>, <em>Label added</em> — Drive: <em>File created</em>, <em>File updated</em>, <em>File trashed</em> — Calendar: <em>Event created</em>, <em>Event updated</em>, <em>Event cancelled</em>. Google OAuth tokens can be revoked silently — watch for the <em>Needs reconnect</em> health badge.</p>
    </div>

    <div className="xre-src-panel xre-src-panel-c">
      <div className="mxp-window" style={{marginTop:'18px'}}>
        <div className="mxp-window-bar">
          <span className="mxp-window-dot" />

          <span className="mxp-window-dot" />

          <span className="mxp-window-dot" />

          <span className="mxp-window-title">App trigger — marketplace apps</span>
        </div>

        <div className="mxp-window-body" style={{minHeight:'auto',padding:'14px 18px'}}>
          <div className="mxp-stream-line mxp-s1"><span className="mxp-stream-time">1.</span><span className="mxp-stream-text">Select an installed app tile. Only apps already in your workspace appear.</span></div>
          <div className="mxp-stream-line mxp-s2"><span className="mxp-stream-time">2.</span><span className="mxp-stream-text">Choose the app-specific event type. Dynamic option fields load from the provider live.</span></div>
          <div className="mxp-stream-line mxp-s3"><span className="mxp-stream-time">3.</span><span className="mxp-stream-text">Save. A post-save panel may show a <em>callback URL</em> and one-time secret header to paste into the provider's dashboard.</span></div>
          <div style={{margin:'10px 0 4px'}}><span className="mxp-stream-event">app handles the subscription · provider-specific event catalog · connect banner if no active connection</span></div>
        </div>
      </div>

      <p style={{fontSize:'13.5px',opacity:0.7,margin:'12px 2px 0'}}>App triggers require the app to be installed from the marketplace first. If the provider connection is revoked, save will fail until reconnected.</p>
    </div>
  </div>
</div>

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6" id="create-a-trigger">
    <span className="actionist-section-eyebrow">Get started</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">Create a trigger.</h2>

    <p className="text-base text-gray-600 dark:text-gray-400 max-w-3xl">
      Open your agent in the studio, select the <strong>Triggers</strong> tab, and click <strong>Create trigger</strong>. The creation drawer has five sections — which ones appear depends on the source type you choose.
    </p>
  </div>

  <Tabs>
    <Tab title="HTTP webhook" icon="webhook">
      <Steps>
        <Step title="Basics — name and source">
          Give the trigger a name (e.g. <em>New Stripe payment</em>). Select the <strong>HTTP webhook</strong> tile. Toggle <strong>Enabled</strong> on to start receiving events immediately after save.
        </Step>

        <Step title="Task — what the agent should do">
          Write a plain-language task description (up to 5,000 characters). This tells the agent what to do with each event. Use <code>\{\{payload.fieldName}}</code> to embed live values from the webhook body — for example:

          ```text task description theme={null}
          Process the new payment for {{payload.customerEmail}} and log it to the CRM.
          ```

          The <strong>Event Data</strong> picker below the field controls which payload fields the agent receives. Without a pasted sample payload, toggle <strong>Send the full payload to the agent</strong> on to forward everything.
        </Step>

        <Step title="Filter — scope the events that matter (optional)">
          Toggle <strong>Filter incoming events</strong> on if you only want the agent to run on a subset of payloads. Add one or more conditions with <strong>Add condition</strong>, choose <strong>All conditions</strong> (AND) or <strong>Any condition</strong> (OR), then pick a field path, an operator, and a value.

          Leave the toggle off to run on every event — useful for prototyping.
        </Step>

        <Step title="Webhook — authentication">
          Choose <strong>Public (no auth)</strong> for open integrations or <strong>Bearer secret</strong> for anything sensitive. If you choose bearer secret, pick an existing credential from your vault or click <strong>+ Create new credential</strong> to create one inline. The credential value is checked against the incoming <code>X-Actionist-Trigger-Secret</code> header on every request.
        </Step>

        <Step title="Delivery — where the result goes">
          Choose how the run result surfaces: <strong>Tell me when it's done</strong> (default — a card appears in Home), <strong>Just run it</strong> (no card; visible on the Triggers page only), or notify via Telegram, Slack, or a specific conversation. Failure cards always go to Home regardless of this setting.
        </Step>

        <Step title="Save">
          Click <strong>Create trigger</strong>. The trigger appears in the list. Expand the row to see the <strong>Webhook URL</strong> with <strong>Copy URL</strong> and <strong>Copy curl</strong> buttons — paste that URL into your external service.
        </Step>
      </Steps>
    </Tab>

    <Tab title="Google" icon="https://cdn.simpleicons.org/google">
      <Steps>
        <Step title="Basics — name and source">
          Name the trigger and select the <strong>Google</strong> tile (labeled <em>Gmail · Drive · Calendar</em>). The Google wizard expands inline in three steps.
        </Step>

        <Step title="Wizard step A — choose a Google service">
          Click <strong>Gmail</strong>, <strong>Drive</strong>, or <strong>Calendar</strong>.

          <AccordionGroup>
            <Accordion title="Gmail event types">
              * <strong>New message</strong> — fires when a message arrives in the inbox
              * <strong>Label added</strong> — fires when a label is applied to a message
            </Accordion>

            <Accordion title="Drive event types">
              * <strong>File created</strong> — a new file appears in the selected folder
              * <strong>File updated</strong> — an existing file is modified
              * <strong>File trashed</strong> — a file is moved to Trash
            </Accordion>

            <Accordion title="Calendar event types">
              * <strong>Event created</strong> — a new calendar event is created
              * <strong>Event updated</strong> — an existing event is modified
              * <strong>Event cancelled</strong> — an event is cancelled
            </Accordion>
          </AccordionGroup>
        </Step>

        <Step title="Wizard step B — event type">
          Select the event type chip that matches when the agent should fire.
        </Step>

        <Step title="Wizard step C — account and resource">
          Pick your Google account from the <strong>Select an account…</strong> dropdown. Then narrow the scope:

          * Gmail: optionally filter by one or more labels
          * Drive: optionally filter by folder and file type
          * Calendar: select the calendar to watch
        </Step>

        <Step title="Task, Filter, and Delivery">
          Complete these sections exactly as described in the HTTP webhook path. The Webhook section does not appear for Google triggers.
        </Step>

        <Step title="Save">
          Click <strong>Create trigger</strong>. Google subscription provisioning begins in the background. A subscription health badge appears on the trigger row once provisioning completes.
        </Step>
      </Steps>
    </Tab>

    <Tab title="App trigger" icon="grid-2x2">
      <Steps>
        <Step title="Basics — name and source">
          Name the trigger and select the app tile under <strong>App trigger</strong>. The list shows only apps you have already installed from the marketplace.
        </Step>

        <Step title="Choose event type and options">
          Select the app-specific event type. Some apps show dynamic option fields (e.g. workspace or channel IDs fetched live from the provider). These fields require an active provider connection — if the connection is revoked, save will fail until reconnected.

          If the app requires a connection and none is linked yet, a banner with a <strong>Connect app</strong> button appears.
        </Step>

        <Step title="Task, Filter, and Delivery">
          Complete these sections as with the other source types.
        </Step>

        <Step title="Save and wire if needed">
          Click <strong>Create trigger</strong>. For apps that use their own webhook infrastructure, a post-save panel may appear with a <strong>callback URL</strong> and a one-time secret header name and value to paste into the provider's dashboard — this step completes the subscription on the provider's side.
        </Step>
      </Steps>
    </Tab>
  </Tabs>

  {/* TODO(image): reactions-create-http — IMAGE SPEC: The 'Create trigger' drawer open in HTTP webhook mode. Show all 5 sections visible (Basics expanded with name 'New Stripe payment' typed and HTTP webhook tile selected; Task section showing instruction template field with {{payload.customerEmail}} text; Filter section with one condition row; Webhook section with 'Bearer secret' selected; Delivery section showing 'Tell me when it\'s done' selected). Drawer floats over the agent studio Triggers tab. — replace this CSS mock with the real screenshot */}

  <div className="mt-10 xre-mock-frame">
    <div className="xre-mock-chrome">
      <div className="mxp-window-bar">
        <span className="mxp-window-dot" />

        <span className="mxp-window-dot" />

        <span className="mxp-window-dot" />

        <span className="mxp-window-title">agent studio · New Stripe payment · Create trigger</span>
      </div>

      <div className="xre-mock-body">
        <div className="xre-mock-drawer">
          <div className="xre-mock-drawer-head">
            <span className="xre-mock-drawer-title">Create trigger</span>
            <span className="xre-mock-close-btn">✕</span>
          </div>

          <div className="xre-mock-section xre-mock-section-open">
            <div className="xre-mock-section-label">Basics</div>
            <div className="xre-mock-row"><span className="xre-mock-field-label">Name</span><span className="xre-mock-input">New Stripe payment</span></div>
            <div className="xre-mock-row" style={{marginTop:'8px'}}><span className="xre-mock-field-label">Source</span></div>

            <div className="xre-mock-tile-row">
              <span className="xre-mock-tile xre-mock-tile-active">HTTP webhook</span>
              <span className="xre-mock-tile">Google</span>
              <span className="xre-mock-tile">App trigger</span>
            </div>
          </div>

          <div className="xre-mock-section">
            <div className="xre-mock-section-label">Task</div>
            <div className="xre-mock-textarea">Process the new payment for <span className="xre-mock-token">\{\{payload.customerEmail}}</span> and log it to the CRM.</div>
          </div>

          <div className="xre-mock-section">
            <div className="xre-mock-section-label">Filter <span className="xre-mock-toggle-chip">On</span></div>

            <div className="xre-mock-filter-row">
              <span className="xre-mock-field-pill">payload.amount</span>
              <span className="xre-mock-op-pill">greater\_than</span>
              <span className="xre-mock-val-pill">500</span>
            </div>
          </div>

          <div className="xre-mock-section">
            <div className="xre-mock-section-label">Webhook auth</div>

            <div className="xre-mock-tile-row">
              <span className="xre-mock-tile">Public (no auth)</span>
              <span className="xre-mock-tile xre-mock-tile-active">Bearer secret</span>
            </div>
          </div>

          <div className="xre-mock-section">
            <div className="xre-mock-section-label">Delivery</div>

            <div className="xre-mock-radio-row">
              <span className="xre-mock-radio xre-mock-radio-on" />

              <span>Tell me when it's done</span>
            </div>

            <div className="xre-mock-radio-row">
              <span className="xre-mock-radio" />

              <span>Just run it</span>
            </div>
          </div>

          <div style={{display:'flex',gap:'8px',paddingTop:'10px',marginTop:'4px'}}>
            <span className="xre-mock-btn-primary">Create trigger</span>
            <span className="xre-mock-btn-ghost">Cancel</span>
          </div>
        </div>
      </div>
    </div>

    <div className="xre-mock-caption">Create trigger drawer — HTTP webhook mode, all five sections</div>
  </div>

  {/* TODO(image): reactions-google-wizard — IMAGE SPEC: The 'Create trigger' drawer with the Google tile selected in Basics. Show the 3-step inline wizard: step A service tiles (Gmail / Drive / Calendar — Gmail highlighted in purple), step B event type chips ('New message' / 'Label added' — New message selected), step C with a Google account picker dropdown open. Wizard is embedded inside the Basics section of the drawer. — replace this CSS mock with the real screenshot */}

  <div className="mt-8 xre-mock-frame">
    <div className="xre-mock-chrome">
      <div className="mxp-window-bar">
        <span className="mxp-window-dot" />

        <span className="mxp-window-dot" />

        <span className="mxp-window-dot" />

        <span className="mxp-window-title">Create trigger · Google source</span>
      </div>

      <div className="xre-mock-body">
        <div className="xre-mock-drawer">
          <div className="xre-mock-drawer-head">
            <span className="xre-mock-drawer-title">Create trigger</span>
            <span className="xre-mock-close-btn">✕</span>
          </div>

          <div className="xre-mock-section xre-mock-section-open">
            <div className="xre-mock-section-label">Basics</div>

            <div className="xre-mock-tile-row">
              <span className="xre-mock-tile">HTTP webhook</span>
              <span className="xre-mock-tile xre-mock-tile-active">Google</span>
              <span className="xre-mock-tile">App trigger</span>
            </div>

            <div className="xre-mock-wizard">
              <div className="xre-mock-wizard-step">
                <span className="xre-mock-wizard-num xre-mock-wizard-done">A</span>

                <div>
                  <div className="xre-mock-wizard-label">Choose a service</div>

                  <div className="xre-mock-tile-row" style={{marginTop:'6px'}}>
                    <span className="xre-mock-tile xre-mock-tile-active">Gmail</span>
                    <span className="xre-mock-tile">Drive</span>
                    <span className="xre-mock-tile">Calendar</span>
                  </div>
                </div>
              </div>

              <div className="xre-mock-wizard-step">
                <span className="xre-mock-wizard-num xre-mock-wizard-done">B</span>

                <div>
                  <div className="xre-mock-wizard-label">Event type</div>

                  <div className="xre-mock-tile-row" style={{marginTop:'6px'}}>
                    <span className="xre-mock-tile xre-mock-tile-active">New message</span>
                    <span className="xre-mock-tile">Label added</span>
                  </div>
                </div>
              </div>

              <div className="xre-mock-wizard-step">
                <span className="xre-mock-wizard-num xre-mock-wizard-active">C</span>

                <div>
                  <div className="xre-mock-wizard-label">Account and scope</div>

                  <div className="xre-mock-dropdown-open" style={{marginTop:'6px'}}>
                    <span>Select a Google account…</span>
                    <span style={{opacity:0.45,fontSize:'11px'}}>▾</span>

                    <div className="xre-mock-dropdown-list">
                      <div className="xre-mock-dropdown-item">[you@example.com](mailto:you@example.com)</div>
                      <div className="xre-mock-dropdown-item">[workspace@example.com](mailto:workspace@example.com)</div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <div className="xre-mock-caption">Google trigger wizard — service, event type, and account picker inline</div>
  </div>
</div>

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">The task section</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">Shape what the agent sees.</h2>

    <p className="text-base text-gray-600 dark:text-gray-400 max-w-3xl">
      The Task section controls three things: <strong>what the agent should do</strong>, <strong>which payload values to embed in the instruction</strong>, and <strong>which event fields to pass alongside it</strong>.
    </p>
  </div>

  <AccordionGroup>
    <Accordion title="Task description" icon="pencil" defaultOpen={true}>
      Write a plain-language instruction for the agent — up to 5,000 characters. Tell it what to do with the event, who to notify, where to store the result. The placeholder shows a minimal example:

      > <em>Summarize the email and add it to my notes</em>

      Event details are automatically appended to what the agent receives — you do not need to describe the payload schema in the task description itself. Use the task description for the <em>goal</em>, not the data.
    </Accordion>

    <Accordion title="Payload templating with the double-brace syntax" icon="code">
      Embed live payload values directly in the instruction using <code>\{\{payload.fieldName}}</code> tokens. When the trigger fires, each token is replaced with the matching field from the incoming event before the agent sees it.

      ```text example instruction theme={null}
      Process the new payment for {{payload.customerEmail}} and log the
      amount ({{payload.amount}}) to the CRM under their account.
      ```

      <strong>What happens when a field is missing:</strong> the token renders as an empty string. Over-templating on fields that are not always present in the payload can produce confusing instructions — use tokens only for fields the provider reliably sends every time.

      The path supports dot notation for nested fields: <code>\{\{payload.customer.email}}</code>.
    </Accordion>

    <Accordion title="Event data picker" icon="list-checks">
      Below the task description, the <strong>Event data</strong> section controls which fields from the incoming event are included in the JSON block the agent receives alongside your task description.

      <strong>Choosing fields:</strong> the picker shows a catalog of fields built from a sample payload. Checked fields are sent; unchecked fields are omitted. The count summary shows how many are included:

      > <em>3 of 5 fields included</em>

      <strong>Leave it as "All fields" unless context window size is a concern.</strong> When no explicit selection is made, all fields are forwarded and new fields added to the event schema in the future are automatically included. An explicit selection is frozen — new fields require re-saving the trigger to include.

      <strong>For HTTP triggers without a sample payload:</strong> toggle <strong>Send the full payload to the agent</strong> on to forward every field from the webhook body. Only top-level fields can be individually selected; nested objects are always sent wholesale.

      <Warning>
        If no fields are checked and "All fields" is also off, the agent receives only your task description — no event data at all. The picker shows a warning: <em>"The agent will only see your task description, not any event data."</em>
      </Warning>
    </Accordion>
  </AccordionGroup>

  {/* TODO(image): reactions-event-data-picker — IMAGE SPEC: The Event data section within the Task area for a Gmail trigger. Show the field list with recommended fields (From, Subject, Email body) checked and two 'Other' fields unchecked. The count summary reads '3 of 5 fields included'. Below it, the 'Agent input preview' block shows the rendered agent input: task description text followed by a JSON block with the three selected fields. — replace this CSS mock with the real screenshot */}

  <div className="mt-8 xre-mock-frame">
    <div className="xre-mock-chrome">
      <div className="mxp-window-bar">
        <span className="mxp-window-dot" />

        <span className="mxp-window-dot" />

        <span className="mxp-window-dot" />

        <span className="mxp-window-title">Task · Event data picker · Gmail trigger</span>
      </div>

      <div className="xre-mock-body">
        <div className="xre-mock-picker">
          <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:'10px'}}>
            <span style={{fontSize:'13.5px',fontWeight:600}}>Event data</span>
            <span className="xre-mock-badge">3 of 5 fields included</span>
          </div>

          <div style={{display:'flex',flexDirection:'column',gap:'3px',marginBottom:'14px'}}>
            <div className="xre-mock-field-item xre-mock-field-checked">
              <span className="xre-mock-checkbox xre-mock-checkbox-on" />

              <span className="xre-mock-field-name">From</span>
              <span className="xre-mock-field-tag">Recommended</span>
            </div>

            <div className="xre-mock-field-item xre-mock-field-checked">
              <span className="xre-mock-checkbox xre-mock-checkbox-on" />

              <span className="xre-mock-field-name">Subject</span>
              <span className="xre-mock-field-tag">Recommended</span>
            </div>

            <div className="xre-mock-field-item xre-mock-field-checked">
              <span className="xre-mock-checkbox xre-mock-checkbox-on" />

              <span className="xre-mock-field-name">Email body</span>
              <span className="xre-mock-field-tag">Recommended</span>
            </div>

            <div className="xre-mock-field-item">
              <span className="xre-mock-checkbox" />

              <span className="xre-mock-field-name" style={{opacity:0.5}}>Message ID</span>
              <span className="xre-mock-field-tag xre-mock-field-tag-other">Other</span>
            </div>

            <div className="xre-mock-field-item">
              <span className="xre-mock-checkbox" />

              <span className="xre-mock-field-name" style={{opacity:0.5}}>Thread ID</span>
              <span className="xre-mock-field-tag xre-mock-field-tag-other">Other</span>
            </div>
          </div>

          <div className="xre-mock-dr-label">Agent input preview</div>

          <div className="xre-mock-preview-block">
            <div className="xre-mock-preview-task">Summarize the email and add it to my notes</div>

            <div style={{padding:'8px 12px',fontFamily:'"JetBrains Mono",monospace',fontSize:'11.5px',whiteSpace:'pre',opacity:0.75,lineHeight:1.55}}>
              {`{\n  "from": "sender@example.com",\n  "subject": "Q3 report attached",\n  "body": "Please find the Q3 report…"\n}`}
            </div>
          </div>
        </div>
      </div>
    </div>

    <div className="xre-mock-caption">Event data picker — 3 of 5 Gmail fields selected with agent input preview</div>
  </div>
</div>

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">Filter section</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">Only wake the agent when it matters.</h2>

    <p className="text-base text-gray-600 dark:text-gray-400 max-w-3xl">
      High-volume event sources like webhooks can fire hundreds of events per day. The filter layer lets the agent sleep through the noise and act only on the events you care about.
    </p>
  </div>

  <p className="text-base text-gray-600 dark:text-gray-400 mb-6 max-w-3xl">
    Toggle <strong>Filter incoming events</strong> on to add rules. The toggle hint describes the default: <em>"Off = run on every event. On = only run on matching payloads."</em> Choose <strong>All conditions</strong> (AND — every rule must match) or <strong>Any condition</strong> (OR — any rule is enough).
  </p>

  <Columns cols={4}>
    <Card title="equals / does not equal" icon="equal">
      Exact string or number match. Works on any field type.
    </Card>

    <Card title="contains" icon="text-search">
      Substring match for string fields. Not available on boolean or object fields.
    </Card>

    <Card title="greater than / less than" icon="chart-column">
      Numeric comparison. Only available when the field type is a number.
    </Card>

    <Card title="exists" icon="circle-check">
      Checks whether the field is present in the payload at all. Use it to gate on optional fields.
    </Card>
  </Columns>

  <Tip>
    For HTTP triggers, paste a sample payload before building filter rules. The <strong>Choose a field…</strong> path picker builds its suggestions from the sample — without one, you can only type custom paths manually (e.g. <code>payload.x.y</code>).
  </Tip>

  <Note>
    Filter evaluation on the client (Dry Run) and on the server (live dispatch) use the same <code>@actionmodel/triggers-runtime</code> package — the preview and production behavior cannot drift. What you see in Dry Run is exactly what fires live.
  </Note>
</div>

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">Test it</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">Verify before you go live.</h2>

    <p className="text-base text-gray-600 dark:text-gray-400 max-w-3xl">
      Two testing tools, two different guarantees. Use both before sending real traffic.
    </p>
  </div>

  <h3 className="text-xl font-semibold tracking-tight text-gray-900 dark:text-gray-50 mb-4 mt-6">Dry run — instant, no side-effects</h3>

  <p className="text-base text-gray-600 dark:text-gray-400 mb-4">
    The <strong>Preview with sample payload</strong> panel renders the exact instruction and evaluates every filter rule against any JSON you paste — instantly, client-side, with no model call and no event stored.
  </p>

  <Steps>
    <Step title="Open the trigger detail">
      Click the trigger row in the Triggers list to expand the detail panel.
    </Step>

    <Step title="Paste a sample payload">
      In the <strong>Preview with sample payload</strong> section, paste a representative JSON payload — a real webhook body, a copied event from your provider's dashboard, or a hand-crafted example.

      For Google triggers, click <strong>Fetch latest event</strong> instead. This pulls the most recent matching event from your Google account in the last 7 days, normalized to the same shape the trigger will receive.
    </Step>

    <Step title="Click Preview">
      The panel instantly shows two things: the <strong>rendered instruction</strong> (with all <code>\{\{payload.x}}</code> tokens resolved), and the <strong>filter result</strong>:

      * <Check>Pass — would dispatch</Check>
      * **Fail — event would be filtered** (the agent would not run)
      * **No filter configured** (filter is off)
    </Step>
  </Steps>

  <h3 className="text-xl font-semibold tracking-tight text-gray-900 dark:text-gray-50 mb-4 mt-10">Live test — real end-to-end run</h3>

  <p className="text-base text-gray-600 dark:text-gray-400 mb-4">
    Once Dry Run looks right, fire a real end-to-end test with the <strong>Run live test</strong> button.
  </p>

  <Warning>
    <strong>Run live test</strong> creates a real event and runs the agent. If the agent can write to external systems — send email, update records — those writes will happen. Ensure the agent's approval mode is set appropriately before running.
  </Warning>

  <Steps>
    <Step title="Enable the agent">
      The <strong>Run live test</strong> button is disabled when the agent is off. Enable the agent in the studio first.
    </Step>

    <Step title="Click Run live test">
      Confirm the dialog, which warns: <em>"This creates a real event and runs the agent."</em>
    </Step>

    <Step title="Follow the run">
      A toast confirms: <em>"Live test fired. Open the chat session to follow it."</em> Switch to the <strong>Recent events</strong> tab to watch the event progress through statuses in real time.
    </Step>
  </Steps>

  {/* TODO(image): reactions-dry-run — IMAGE SPEC: The 'Preview with sample payload' panel within the trigger detail panel. Show a Stripe-style JSON payload pasted in the textarea (with amount, customerEmail fields). The 'Rendered instruction' section shows the instruction with {{payload.customerEmail}} resolved to 'alex@example.com'. The filter result badge reads 'Pass — would dispatch' in green. — replace this CSS mock with the real screenshot */}

  <div className="mt-8 xre-mock-frame">
    <div className="xre-mock-chrome">
      <div className="mxp-window-bar">
        <span className="mxp-window-dot" />

        <span className="mxp-window-dot" />

        <span className="mxp-window-dot" />

        <span className="mxp-window-title">trigger · stripe-payments · Preview with sample payload</span>
      </div>

      <div className="xre-mock-body">
        <div className="xre-mock-dry-run">
          <div className="xre-mock-dr-col">
            <div className="xre-mock-dr-label">Sample payload</div>

            <div className="xre-mock-dr-json">
              {`{\n  "amount": 1200,\n  "customerEmail": "alex@example.com",\n  "status": "paid",\n  "currency": "usd"\n}`}
            </div>

            <div className="xre-mock-btn-primary" style={{marginTop:'10px',padding:'5px 12px',fontSize:'11.5px'}}>Preview</div>
          </div>

          <div className="xre-mock-dr-col">
            <div className="xre-mock-dr-label">Rendered instruction</div>

            <div className="xre-mock-dr-rendered">
              Process the new payment for <strong>[alex@example.com](mailto:alex@example.com)</strong> (amount: <strong>\$1200</strong>) and log it to the CRM.
            </div>

            <div className="xre-mock-dr-label" style={{marginTop:'14px'}}>Filter result</div>

            <div className="xre-mock-dr-pass">
              <span style={{width:'9px',height:'9px',borderRadius:'999px',background:'#6EE3B5',flex:'none',display:'inline-block'}} />

              Pass — would dispatch
            </div>
          </div>
        </div>
      </div>
    </div>

    <div className="xre-mock-caption">Dry run — payload tokens resolved, filter result: Pass</div>
  </div>
</div>

<hr className="xc-divider" />

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">Diagnostics</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">Every event leaves a trace.</h2>

    <p className="text-base text-gray-600 dark:text-gray-400 max-w-3xl">
      The <strong>Recent events</strong> tab on each trigger keeps a full log of every inbound event: its status, the rendered payload, the filter outcome, and a direct link to the agent run transcript.
    </p>
  </div>

  <div className="mxp-window">
    <div className="mxp-window-bar">
      <span className="mxp-window-dot" />

      <span className="mxp-window-dot" />

      <span className="mxp-window-dot" />

      <span className="mxp-window-title">trigger · stripe-payments · recent events</span>
    </div>

    <div className="mxp-window-body">
      <div className="mxp-s1"><span className="mxp-stream-event">event · 10:42 · amount: 1200, status: paid</span></div>
      <div className="mxp-stream-line mxp-s2"><span className="mxp-stream-time">10:42</span><span className="mxp-stream-text">Received → Ready → Claimed → Dispatched → <em>Completed</em>. Run transcript linked.</span></div>
      <div className="mxp-s3"><span className="mxp-stream-event">event · 10:55 · amount: 80, status: refunded</span></div>
      <div className="mxp-stream-line mxp-s4"><span className="mxp-stream-time">10:55</span><span className="mxp-stream-text" style={{opacity:0.55}}><em>Filtered</em> — amount 80 is not greater than 500. Agent not invoked. Payload retained 7 days.</span></div>
      <div className="mxp-s5"><span className="mxp-stream-event">event · 11:03 · amount: 2400, status: paid</span></div>
      <div className="mxp-stream-line mxp-s6"><span className="mxp-stream-time">11:03</span><span className="mxp-stream-text">Received → Ready → Claimed → Dispatched → <em>Completed</em>. Payment logged to CRM.<span className="mxp-caret" /></span></div>
    </div>
  </div>

  <p className="text-base text-gray-600 dark:text-gray-400 mt-6 mb-6">
    The table auto-refreshes every 5 seconds so you can watch a live test progress in real time. Click any row to open the <strong>Event detail</strong> drawer.
  </p>

  <AccordionGroup>
    <Accordion title="What the event detail drawer shows" icon="panel-right" defaultOpen={true}>
      * <strong>Payload</strong> — the raw event body, with sensitive keys automatically redacted
      * <strong>Filter result</strong> — which rule passed or dropped the event
      * <strong>Dispatch info</strong> — the agent, model, and run metadata
      * <strong>Open chat session</strong> — a direct link to the full agent run transcript for completed or failed events
    </Accordion>

    <Accordion title="Reading the status badges" icon="badge-check">
      | Status                      | Meaning                                                     |
      | --------------------------- | ----------------------------------------------------------- |
      | <strong>Received</strong>   | Event arrived; awaiting processing                          |
      | <strong>Ready</strong>      | Event parsed and queued; awaiting claim by the worker       |
      | <strong>Claimed</strong>    | Worker picked it up; agent run starting                     |
      | <strong>Dispatched</strong> | Agent is actively running                                   |
      | <strong>Completed</strong>  | Agent run finished successfully                             |
      | <strong>Failed</strong>     | Agent run threw an error; details in drawer                 |
      | <strong>Filtered</strong>   | Event did not match the filter rules; agent was not invoked |
      | <strong>Expired</strong>    | Event was not claimed within 7 days and will not run        |
      | <strong>Cancelled</strong>  | Run was stopped before completion                           |
    </Accordion>

    <Accordion title="Diagnosing a trigger that fires but nothing happens" icon="search">
      If a trigger receives events but the agent never seems to run, the most likely cause is that events are being filtered. Check the <strong>Recent events</strong> tab for rows with a <strong>Filtered</strong> badge and click the row — the drawer shows exactly which filter condition dropped it.
    </Accordion>
  </AccordionGroup>

  {/* TODO(image): reactions-event-history — IMAGE SPEC: The Recent events tab showing a table with ~6 event rows and mixed status badges: two 'Completed' (green), one 'Filtered' (gray), one 'Failed' (red), one 'Received' (blue). The Event detail drawer is open on the right side for the Filtered row, showing the raw payload (one field redacted), the filter result 'Filtered out — amount less than 500', and a greyed-out Open chat session link. — replace this CSS mock with the real screenshot */}

  <div className="mt-8 xre-mock-frame">
    <div className="xre-mock-chrome">
      <div className="mxp-window-bar">
        <span className="mxp-window-dot" />

        <span className="mxp-window-dot" />

        <span className="mxp-window-dot" />

        <span className="mxp-window-title">trigger · stripe-payments · Recent events</span>
      </div>

      <div className="xre-mock-body xre-mock-body-split">
        <div className="xre-mock-events-table">
          <div style={{display:'grid',gridTemplateColumns:'48px 1fr 90px',gap:'8px',padding:'4px 8px',fontSize:'10.5px',fontWeight:700,letterSpacing:'0.06em',textTransform:'uppercase',opacity:0.4}}>
            <span>Time</span><span>Payload</span><span>Status</span>
          </div>

          <div className="xre-mock-event-row">
            <span className="xre-mock-time">11:03</span>
            <span className="xre-mock-payload-snip">amount: 2400, status: paid</span>
            <span className="xre-mock-status-badge xre-mock-s-completed">Completed</span>
          </div>

          <div className="xre-mock-event-row">
            <span className="xre-mock-time">10:55</span>
            <span className="xre-mock-payload-snip">amount: 80, status: refunded</span>
            <span className="xre-mock-status-badge xre-mock-s-filtered">Filtered</span>
          </div>

          <div className="xre-mock-event-row">
            <span className="xre-mock-time">10:42</span>
            <span className="xre-mock-payload-snip">amount: 1200, status: paid</span>
            <span className="xre-mock-status-badge xre-mock-s-completed">Completed</span>
          </div>

          <div className="xre-mock-event-row">
            <span className="xre-mock-time">10:18</span>
            <span className="xre-mock-payload-snip">amount: 560, status: failed</span>
            <span className="xre-mock-status-badge xre-mock-s-failed">Failed</span>
          </div>

          <div className="xre-mock-event-row">
            <span className="xre-mock-time">10:02</span>
            <span className="xre-mock-payload-snip">amount: 3100, status: paid</span>
            <span className="xre-mock-status-badge xre-mock-s-received">Received</span>
          </div>
        </div>

        <div className="xre-mock-detail-drawer">
          <div className="xre-mock-detail-head">Event detail</div>

          <div className="xre-mock-detail-section">
            <div className="xre-mock-detail-label">Payload</div>

            <div className="xre-mock-detail-json">
              {`{\n  "amount": 80,\n  "status": "refunded",\n  "email": "[redacted]"\n}`}
            </div>
          </div>

          <div className="xre-mock-detail-section">
            <div className="xre-mock-detail-label">Filter result</div>
            <div className="xre-mock-detail-filter-drop">Filtered out — amount less than 500</div>
          </div>

          <div className="xre-mock-detail-section">
            <div className="xre-mock-detail-label">Open chat session</div>
            <div style={{opacity:0.35,fontSize:'11.5px',textDecoration:'line-through'}}>Not available — agent was not invoked</div>
          </div>
        </div>
      </div>
    </div>

    <div className="xre-mock-caption">Recent events — mixed status badges with detail drawer open on a Filtered row</div>
  </div>
</div>

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">Operations</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">Keeping triggers healthy.</h2>
  </div>

  <Tabs>
    <Tab title="HTTP webhook" icon="webhook">
      <h3 className="text-xl font-semibold tracking-tight text-gray-900 dark:text-gray-50 mb-3 mt-2">Rotate the webhook URL</h3>

      <p className="text-base text-gray-600 dark:text-gray-400 mb-4">
        Rotate the URL when a secret is compromised or when you want to revoke access from a specific producer. The old URL stops accepting requests the moment you confirm.
      </p>

      <Steps>
        <Step title="Select the trigger row">
          Expand the trigger in the list to see the detail panel.
        </Step>

        <Step title="Click Rotate URL">
          A confirmation dialog appears with the warning: <em>"The current URL will stop working immediately. Update any external integrations after rotating."</em>
        </Step>

        <Step title="Confirm">
          Click <strong>Rotate URL</strong>. The new URL appears immediately. A toast confirms: <em>"New webhook URL generated."</em> Update every external producer before they send the next event.
        </Step>
      </Steps>

      <Warning>
        Rotation takes effect with no grace period. Any request sent to the old URL after you rotate will be rejected — plan accordingly, especially for high-volume producers.
      </Warning>

      <h3 className="text-xl font-semibold tracking-tight text-gray-900 dark:text-gray-50 mb-3 mt-8">Provisioning badge</h3>

      <p className="text-base text-gray-600 dark:text-gray-400">
        The trigger row shows a badge indicating whether the webhook endpoint is ready. If it shows <strong>Provisioning failed</strong>, a <strong>Retry</strong> button appears — click it to re-attempt provisioning without deleting the trigger.
      </p>
    </Tab>

    <Tab title="Google" icon="https://cdn.simpleicons.org/google">
      <h3 className="text-xl font-semibold tracking-tight text-gray-900 dark:text-gray-50 mb-3 mt-2">Google subscription health</h3>

      <p className="text-base text-gray-600 dark:text-gray-400 mb-6">
        Google push subscriptions expire and can be revoked. Actionist shows a health badge on each Google trigger row so you can spot problems before events start silently dropping.
      </p>

      <Columns cols={4}>
        <Card title="Healthy" icon="circle-check">
          <Badge color="green" size="sm">Healthy</Badge>
          <p className="mt-3 text-sm">Subscription is active and receiving events normally.</p>
        </Card>

        <Card title="Renewing" icon="loader">
          <Badge color="blue" size="sm">Renewing</Badge>
          <p className="mt-3 text-sm">Subscription is being automatically renewed. Brief gap in delivery is possible.</p>
        </Card>

        <Card title="Needs reconnect" icon="triangle-alert">
          <Badge color="yellow" size="sm">Needs reconnect</Badge>
          <p className="mt-3 text-sm">OAuth token was revoked. No events will be received until reconnected.</p>
        </Card>

        <Card title="Stopped / Expired" icon="circle-x">
          <Badge color="red" size="sm">Stopped</Badge>
          <p className="mt-3 text-sm">Subscription has ended. Reconnect to resume delivery.</p>
        </Card>
      </Columns>

      <h3 className="text-xl font-semibold tracking-tight text-gray-900 dark:text-gray-50 mb-3 mt-8">How to reconnect</h3>

      <Steps>
        <Step title="Find the trigger">
          Look for a trigger row with a <strong>Needs reconnect</strong> badge.
        </Step>

        <Step title="Open the edit modal">
          Click the pencil icon to edit the trigger.
        </Step>

        <Step title="Click Reconnect Google account">
          The button appears in the Google wizard section. An OAuth window opens in your browser.
        </Step>

        <Step title="Complete the OAuth flow">
          Authorize the account. The badge returns to <strong>Healthy</strong> and event delivery resumes. You do not need to delete and recreate the trigger.
        </Step>
      </Steps>
    </Tab>
  </Tabs>
</div>

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">Reference</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">Limits and defaults.</h2>
  </div>

  <div className="mxs-limits">
    <div className="mxs-limit"><b>5,000</b><span>characters max per task description</span></div>
    <div className="mxs-limit"><b className="xc-count" style={{"--xc-to":"256"}} data-suffix=" KB"><span className="xc-real">256 KB</span></b><span>max HTTP payload size</span></div>
    <div className="mxs-limit"><b className="xc-count" style={{"--xc-to":"7"}} data-suffix=" days"><span className="xc-real">7 days</span></b><span>event TTL — unclaimed events expire and will not dispatch</span></div>
    <div className="mxs-limit"><b className="xc-count" style={{"--xc-to":"7"}} data-suffix=" days"><span className="xc-real">7 days</span></b><span>raw payload retention window</span></div>
    <div className="mxs-limit"><b className="xc-count" style={{"--xc-to":"30"}} data-suffix=" min"><span className="xc-real">30 min</span></b><span>max agent run duration per event</span></div>
    <div className="mxs-limit"><b>\~45 s</b><span>worker poll interval (±5s jitter)</span></div>
    <div className="mxs-limit"><b className="xc-count" style={{"--xc-to":"60"}} data-suffix=" s"><span className="xc-real">60 s</span></b><span>HTTP deduplication window — identical payloads within window fire the agent once</span></div>
    <div className="mxs-limit"><b className="xc-count" style={{"--xc-to":"7"}} data-suffix=" days"><span className="xc-real">7 days</span></b><span>Google sample event lookback for Fetch latest event</span></div>
  </div>

  <Note>
    The \~45-second poll interval means an event delivered to the backend can take up to 45 seconds before the agent starts processing. Design time-sensitive workflows accordingly.
  </Note>

  <div className="mt-8" />

  | Thing                                      | Default                                         |
  | ------------------------------------------ | ----------------------------------------------- |
  | Delivery target                            | Home (result card in agent's Home conversation) |
  | Missing <code>\{\{payload.x}}</code> field | Renders as empty string                         |
  | Filter toggle                              | Off — run on every event                        |
  | Event data                                 | All fields forwarded when no explicit selection |
</div>

<hr className="xc-divider" />

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">Tips</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">Best practices.</h2>
  </div>

  <AccordionGroup>
    <Accordion title="Paste a real payload before adding filter rules (HTTP)" icon="clipboard-paste">
      The <strong>Choose a field…</strong> path picker and the Event Data Picker both build their suggestions from the sample payload you provide. Without a pasted sample, only top-level keys appear and you cannot get field-level operator suggestions. Paste a real payload first, then build the filter.
    </Accordion>

    <Accordion title="Leave Event Data as 'All fields' unless token count is a concern" icon="list">
      The all-fields setting (<code>undefined</code> in the data model) auto-includes fields added to the event catalog later. An explicit selection is frozen — any new fields the provider adds require you to re-save the trigger to include them. Prefer all-fields for low-stakes triggers; use an explicit selection only when the payload is large enough to cause context window issues.
    </Accordion>

    <Accordion title="Use Dry Run before connecting real traffic" icon="flask-conical">
      Paste a representative payload and confirm both the rendered instruction and the filter result look correct. Dry Run is instant, client-side, and has zero side-effects. There is no reason not to use it — it catches template errors and filter misconfiguration before the first real event arrives.
    </Accordion>

    <Accordion title="Use Bearer Secret authentication for any sensitive webhook" icon="shield-check">
      Store the secret in the Credentials Vault; the credential's value is checked against the incoming <code>X-Actionist-Trigger-Secret</code> header on every request. <strong>Public (no auth)</strong> is fine for low-stakes event buses; anything involving PII or financial data should always use bearer secret.
    </Accordion>

    <Accordion title="Rotate the URL only when a secret is compromised" icon="rotate-ccw">
      Rotation takes immediate effect with no grace period. Update all external producers before rotating if you can — not after. If you are responding to a leaked secret and cannot coordinate, accept a brief delivery gap and update producers immediately after rotation.
    </Accordion>

    <Accordion title="Template the double-brace syntax sparingly" icon="braces">
      Missing tokens render as empty strings silently. Over-templating on fields that are not reliably present produces confusing agent instructions. Use tokens only for fields your provider sends consistently on every event of that type.
    </Accordion>

    <Accordion title="Watch the 'Filtered' badge before assuming a trigger is broken" icon="funnel">
      If the trigger receives events but the agent never runs, check the <strong>Recent events</strong> tab for <strong>Filtered</strong> status rows. The Event detail drawer shows exactly which condition dropped the event — usually a value mismatch or a field that was not present in the payload.
    </Accordion>

    <Accordion title="Monitor Google subscription health" icon="activity">
      Google OAuth tokens can be revoked silently — by the user, by a password reset, or by a security policy. The <strong>Needs reconnect</strong> badge is the only signal. Check trigger rows periodically and reconnect immediately when it appears; the subscription stops delivering events the moment the token is invalidated.
    </Accordion>
  </AccordionGroup>
</div>

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">Real-world wins</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">The business moves while you are elsewhere.</h2>

    <p className="text-base text-gray-600 dark:text-gray-400 max-w-3xl">
      Three event-driven reactions that eliminate the gap between something happening and your team knowing about it.
    </p>
  </div>

  <div className="xc-recipes">
    <div className="xc-recipe">
      <div className="xc-recipe-who">
        <i style={{background:'linear-gradient(135deg,#24A4FF,#6C37FF)'}} />

        Dario · Sales
      </div>

      <div className="xc-recipe-flow">
        <em>New-lead webhook fires</em> the moment a form submission lands. Dario's agent reads the payload, checks the CRM for context, and <em>drafts a personalised intro email</em> — ready to review before the lead has closed the tab.
      </div>

      <span className="xc-recipe-save">live in \< 2 min after the first webhook fires</span>
    </div>

    <div className="xc-recipe">
      <div className="xc-recipe-who">
        <i style={{background:'linear-gradient(135deg,#FD840C,#E3008E)'}} />

        Felix · Finance & Ops
      </div>

      <div className="xc-recipe-flow">
        <em>Gmail: New message</em> with "invoice" in the subject wakes Felix's agent. It extracts the amount and due date, logs them to the expenses tracker, and <em>schedules a payment-reminder task</em> for three days before the deadline.
      </div>

      <span className="xc-recipe-save">saves \~3 hrs/wk on manual invoice triage</span>
    </div>

    <div className="xc-recipe">
      <div className="xc-recipe-who">
        <i style={{background:'linear-gradient(135deg,#486EFF,#9000FF)'}} />

        Sam · Solo founder
      </div>

      <div className="xc-recipe-flow">
        <em>Drive: File created</em> in the Contracts folder triggers Sam's agent. It reads the filename and folder, adds a <em>needs-legal-review flag</em> to the project tracker, and posts a summary to the legal Slack channel — all before Sam is back from the meeting.
      </div>

      <span className="xc-recipe-save">zero manual handoffs on contract intake</span>
    </div>
  </div>

  <div className="xc-quote" style={{marginTop:'32px'}}>
    <p>You were in a two-hour meeting. Three things happened in the world. Your agents dealt with all of them.</p>
    <span>illustrative — Reactions page, Actionist docs</span>
  </div>
</div>

<div className="w-full py-10 xc-rise">
  <div className="actionist-cta-panel xc-border">
    <div className="relative z-10 flex flex-col items-center gap-5 max-w-2xl mx-auto">
      <span className="actionist-eyebrow">Always on</span>

      <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-white leading-tight">
        Turn your agent into an autonomous responder.
      </h2>

      <p className="text-base md:text-lg text-white/85">
        Pick a source, write a task, and let the world wake your agent instead of you.
      </p>

      <div className="flex flex-wrap items-center justify-center gap-3 pt-2">
        <a className="actionist-cta-btn-primary" href="https://app.actionist.ai" target="_blank" rel="noreferrer">
          Open the Dashboard

          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
            <path d="M5 12h14" />

            <path d="m12 5 7 7-7 7" />
          </svg>
        </a>

        <a className="actionist-cta-btn-secondary" href="mailto:support@actionist.ai">
          Email the team
        </a>
      </div>
    </div>
  </div>
</div>

<div className="w-full pt-2 pb-6">
  <p className="actionist-trust-line">
    Event-driven, not polling-based — no infrastructure to host or monitor.<br />
    Filter before dispatch · Full audit trail · Desktop-only feature.
  </p>
</div>

<div className="w-full pb-16 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">Keep going</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">Next steps.</h2>
  </div>

  <Columns cols={4}>
    <Card title="Schedules" icon="calendar-clock" href="/Folders/schedules">
      Time-based automation — the complement to event-based. Recurring jobs, cron expressions, and timezone-aware scheduling.
    </Card>

    <Card title="Credentials" icon="key" href="/Folders/credentials">
      Store bearer secrets and OAuth tokens securely in the Credentials Vault — referenced by triggers without exposing raw values.
    </Card>

    <Card title="Agents" icon="users" href="/Folders/agents">
      Configure approval mode, enable or disable the agent, and understand how agent state affects triggered runs and Live Test.
    </Card>

    <Card title="Marketplace" icon="grid-2x2" href="/Folders/marketplace">
      Browse and install the apps that provide App trigger sources — and explore what each app's event catalog includes.
    </Card>
  </Columns>
</div>
