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

# Assistant examples: four tasks worked end to end

> Four worked ModuleX Assistant tasks you can copy: research and summarize, inbox triage, draft and send, and a multi-tool task. See the plain-language prompt, the steps the Assistant takes, where it pauses for approval, and the result.

export const MediaEmbed = ({id, type = 'screenshot', caption = '', ext, ratio = '16 / 9'}) => {
  const isVideo = type === 'video' || type === 'app_video';
  const resolvedExt = ext || (isVideo ? 'mp4' : type === 'screenshot' ? 'webp' : 'svg');
  const src = 'https://media.modulex.dev/' + id + '.' + resolvedExt;
  const [status, setStatus] = useState('loading');
  const [isDev, setIsDev] = useState(false);
  const [inView, setInView] = useState(false);
  const boxRef = useRef(null);
  useEffect(() => {
    if (typeof window === 'undefined') return;
    const h = window.location.hostname;
    setIsDev(h === 'localhost' || h === '127.0.0.1' || h.endsWith('.mintlify.app'));
  }, []);
  useEffect(() => {
    if (inView) return;
    if (typeof IntersectionObserver === 'undefined') {
      setInView(true);
      return;
    }
    const el = boxRef.current;
    if (!el) return;
    const io = new IntersectionObserver(entries => {
      if (entries.some(e => e.isIntersecting)) {
        setInView(true);
        io.disconnect();
      }
    }, {
      rootMargin: '300px'
    });
    io.observe(el);
    return () => io.disconnect();
  }, [inView]);
  if (status === 'missing') {
    if (!isDev) return null;
    return <div style={{
      display: 'flex',
      flexDirection: 'column',
      justifyContent: 'center',
      gap: '0.4rem',
      padding: '1rem 1.25rem',
      margin: '1.25rem 0',
      width: '100%',
      aspectRatio: ratio,
      boxSizing: 'border-box',
      border: '1px dashed rgba(128,128,128,0.45)',
      borderRadius: '0.75rem',
      background: 'rgba(128,128,128,0.06)',
      color: 'currentColor',
      fontSize: '0.85rem',
      lineHeight: 1.45
    }}>
        <div style={{
      display: 'flex',
      alignItems: 'center',
      gap: '0.5rem',
      opacity: 0.75
    }}>
          <span aria-hidden="true">🎬</span>
          <code style={{
      fontSize: '0.75rem'
    }}>{id}</code>
          <span style={{
      fontSize: '0.65rem',
      textTransform: 'uppercase',
      letterSpacing: '0.04em',
      padding: '0.1rem 0.4rem',
      borderRadius: '0.4rem',
      background: 'rgba(128,128,128,0.18)'
    }}>
            {type}
          </span>
        </div>
        <div style={{
      opacity: 0.9
    }}>{caption || 'Media not uploaded yet.'}</div>
        <div style={{
      fontSize: '0.7rem',
      opacity: 0.5
    }}>
          Upload to R2 as <code>{id}.{resolvedExt}</code> — preview only, hidden in production.
        </div>
      </div>;
  }
  const mediaStyle = {
    display: status === 'loaded' ? 'block' : 'none',
    width: '100%',
    height: 'auto',
    borderRadius: '0.75rem'
  };
  const media = isVideo ? <video src={inView ? src : undefined} autoPlay loop muted playsInline preload="metadata" onLoadedData={() => setStatus('loaded')} onError={() => setStatus('missing')} style={mediaStyle} /> : <img src={inView ? src : undefined} alt={caption} onLoad={() => setStatus('loaded')} onError={() => setStatus('missing')} style={mediaStyle} />;
  return <figure style={{
    margin: '1.25rem 0'
  }}>
      <div ref={boxRef} style={status === 'loaded' ? {
    width: '100%'
  } : {
    width: '100%',
    aspectRatio: ratio,
    borderRadius: '0.75rem',
    background: 'rgba(128,128,128,0.06)'
  }}>
        {media}
      </div>
      {status === 'loaded' && caption ? <figcaption style={{
    marginTop: '0.5rem',
    textAlign: 'center',
    fontSize: '0.85rem',
    opacity: 0.7
  }}>
          {caption}
        </figcaption> : null}
    </figure>;
};

These are four common tasks people give the [Assistant](/assistant/overview), each shown end to end: the plain-language prompt you type, the steps the Assistant works through, the point where it pauses for your approval, and what you get back. Copy a prompt, adapt it to your own services, and go.

Every example uses tools you have already connected to ModuleX. If a task needs a service you have not connected yet, the Assistant asks you to add it right inside the chat — see [authentication and credentials](/integrations/authentication). For the bigger picture of what the Assistant is, start with the [Assistant overview](/assistant/overview). For reusable patterns that combine the Assistant with workflows and knowledge, see [recipes](/power-using/recipes).

<MediaEmbed id="MX-MEDIA-3290" type="app_video" caption={"One Assistant example carried out end to end in the chat surface, from prompt to result."} />

## How to read these examples

Each example follows the same shape, so once you have read one you can skim the rest.

<CardGroup cols={3}>
  <Card title="The prompt" icon="message-square">
    The exact plain-language message you send to start the [turn](/assistant/how-it-works). One message per turn — there is no form to fill in.
  </Card>

  <Card title="What happens" icon="list-checks">
    The steps the Assistant works through: it reads, calls tools one at a time, and may pause to ask you something. You watch it all [stream live](/assistant/streaming).
  </Card>

  <Card title="The result" icon="circle-check">
    What you get back at the end of the turn — an answer, a summary, or a confirmed action.
  </Card>
</CardGroup>

<Note>
  The Assistant calls one tool at a time and pauses before anything sensitive — sending a message, creating a record, deleting data. Nothing risky runs until you approve it on the [human-in-the-loop](/assistant/human-in-the-loop) card. The tools named below (GitHub, Slack, Gmail, Linear, and so on) are examples; the Assistant uses whatever you have connected from the [integration catalog](/integrations/catalog).
</Note>

## Example 1 — Research and summarize

Pull together information that lives across your connected services, or in your own documents, and get a tidy summary back. This is a read-only task, so the Assistant does not need to pause for approval — it gathers and reports.

<Steps>
  <Step title="The prompt">
    Type a request that names what to find and how you want it summarized.

    ```text Prompt theme={null}
    Find the five newest open issues in the acme/api repository and summarize them as a short bulleted list, grouped by label.
    ```
  </Step>

  <Step title="What happens">
    The Assistant works the task one tool at a time, streaming its progress:

    1. It checks which services are connected and confirms GitHub is available.
    2. It calls a read action to fetch the newest open issues from `acme/api`.
    3. It reads the results, groups them by label, and writes the summary.

    Because nothing is created or sent, the Assistant runs straight through without an approval pause.
  </Step>

  <Step title="The result">
    A short, grouped summary in the chat — for example, a `bug` group and a `feature` group, each with a one-line description per issue and a link. You can ask a follow-up in the same chat, such as "Now draft a reply to the top bug," which becomes Example 3.
  </Step>
</Steps>

<Accordion title="Grounding the answer in your own documents">
  You can point the same kind of task at your [knowledge bases](/concepts/knowledge-rag) instead of, or alongside, a live service. Ask something like "Summarize what our onboarding guide says about SSO," and the Assistant searches your connected knowledge and answers from your own documents. Searching managed knowledge carries a small credit cost — see [credits and metering](/billing/credits).
</Accordion>

<MediaEmbed id="MX-MEDIA-3291" type="screenshot" caption={"A research-and-summarize turn in the chat: tool-call chips for the read action, then a grouped bulleted summary."} />

## Example 2 — Inbox and triage

Sort through incoming items — emails, support tickets, or issues — and turn a noisy inbox into a ranked, labeled list you can act on. Reading and ranking are safe, so the Assistant does that on its own; if you ask it to apply labels or change anything, it pauses for approval first.

<Steps>
  <Step title="The prompt">
    Describe what to triage and the order you want.

    ```text Prompt theme={null}
    Review my unread support tickets from the last 24 hours, rank them by urgency, and tell me which three I should answer first and why.
    ```
  </Step>

  <Step title="What happens">
    1. The Assistant confirms your support tool is connected.
    2. It calls a read action to list the unread tickets from the last day.
    3. It reads each ticket, judges urgency from the content, and ranks them.
    4. It reports the top three with a one-line reason each.

    If your prompt also asked it to *act* — "and add the `urgent` label to the top three" — the Assistant would stop on an approval card before changing any ticket, because applying a label is a write action.
  </Step>

  <Step title="The result">
    A ranked shortlist with reasons, ready for you to act on. If you approved label changes, you also get a confirmation of exactly what was changed.
  </Step>
</Steps>

<Accordion title="Why triage pauses only when it changes something">
  Listing and reading items never changes your data, so the Assistant does those steps without interrupting you. The moment a step would *write* — apply a label, archive a thread, reassign a ticket — it pauses and shows you precisely what it is about to do. You can approve, choose a different option, or decline. See [human-in-the-loop](/assistant/human-in-the-loop) for the question and approval kinds.
</Accordion>

## Example 3 — Draft and send

Have the Assistant gather context, write something for you, and send it — with an approval step in the middle so you always see the final draft before it goes out. This is the canonical "pause before a sensitive action" pattern.

<Steps>
  <Step title="The prompt">
    Say what to write, to whom, and where to send it.

    ```text Prompt theme={null}
    Draft a friendly reply to the newest message in the #support Slack channel letting them know we are looking into it, then send it.
    ```
  </Step>

  <Step title="What happens">
    1. The Assistant confirms Slack is connected.
    2. It calls a read action to fetch the newest message in `#support` for context.
    3. It writes a draft reply and shows it to you.
    4. It pauses on an approval card — the send does not happen yet.
  </Step>

  <Step title="You approve (or edit)">
    Review the draft on the approval card. Approve to send it as written, decline to stop, or reply in the chat with a change such as "make it shorter" and the Assistant revises and asks again. Only the person who started the turn can answer its question.
  </Step>

  <Step title="The result">
    Once you approve, the Assistant calls the send action and confirms the message went out — with a link to it where the service provides one.
  </Step>
</Steps>

<Warning>
  The Assistant always pauses before a sensitive action. A draft is never sent, a record is never created, and nothing is deleted until you approve it on the card. If you decline, the turn ends without acting. Read more about how the pause works in [human-in-the-loop](/assistant/human-in-the-loop).
</Warning>

<MediaEmbed id="MX-MEDIA-3292" type="screenshot" caption={"The approval card mid-turn, showing the drafted message and approve/decline controls before the send."} />

## Example 4 — Multi-tool task

Chain several services together in one request. The Assistant plans the steps, calls each tool in turn, carries results from one step into the next, and pauses before the one step that writes.

<Steps>
  <Step title="The prompt">
    Describe the whole task in one message — the Assistant figures out the order.

    ```text Prompt theme={null}
    Find my three newest GitHub issues labeled "bug", create a Linear ticket for each one, and post a summary of what you created to the #engineering Slack channel.
    ```
  </Step>

  <Step title="What happens">
    The Assistant breaks the request into ordered steps and works them one tool at a time:

    1. It reads the three newest `bug` issues from GitHub.
    2. It pauses on an approval card before creating Linear tickets, since creating records is a write action.
    3. After you approve, it creates one Linear ticket per issue, carrying each issue's title and link into the new ticket.
    4. It pauses again before posting to Slack, then — once approved — posts a summary of the tickets it created.
  </Step>

  <Step title="The result">
    Three new Linear tickets and one Slack summary post, plus a final message in the chat recapping everything it did and linking to each item it created.
  </Step>
</Steps>

<Accordion title="What if a service is not connected yet?">
  If the task needs a service you have not connected — say Linear in the example above — the Assistant pauses and asks you to add the credential, including starting an OAuth connection, without leaving the chat. Once you connect it, the Assistant picks the task back up automatically. See [using tools](/assistant/using-tools) and [authentication and credentials](/integrations/authentication).
</Accordion>

<Accordion title="When to turn a task into a reusable workflow">
  The Assistant does a task once, right now. If you find yourself running the same multi-tool task often — or you want to schedule it, trigger it from an API, or add branching and retries — rebuild it as a reusable [workflow](/concepts/workflows-and-runs) with the [AI Composer](/concepts/ai-composer) or the [workflow builder](/workflow-builder/overview). Many people prove a task with the Assistant first, then promote it to a workflow.
</Accordion>

## Good to know before you start

A few things shape every example above.

<CardGroup cols={2}>
  <Card title="One message per turn" icon="message-square">
    Each turn is a single text message — there is no multi-part form and no file upload. Send your whole request in one message; the Assistant plans the steps from there.
  </Card>

  <Card title="One question at a time" icon="hand">
    A chat holds only one open question at a time. While the Assistant is waiting for your approval, it will not start another turn on that chat — finish the current step first.
  </Card>

  <Card title="Owner or admin role" icon="shield">
    As built today, you need the **owner** or **admin** role in the [organization](/concepts/organizations-roles) to use the Assistant. The retired `member` role is not a current role. See [permissions and limits](/assistant/permissions-and-limits).
  </Card>

  <Card title="Each turn uses credits" icon="coins">
    A turn is metered like any managed run, with model token usage recorded separately. If your allowance is exhausted, the turn is declined before anything runs. See [credits and the billing model](/concepts/credits-billing).
  </Card>
</CardGroup>

## Try an example from your own code

You can drive any of these tasks programmatically. A turn starts with one authenticated request that creates or continues a chat and returns a `stream_url`; you open that stream to watch the Assistant work, exactly as you would in the app. Every request uses `Authorization: Bearer mx_live_…` plus your `X-Organization-ID` header — see [authentication](/api-reference/authentication).

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.modulex.dev/assistant/chat \
    -H "Authorization: Bearer mx_live_your_api_key" \
    -H "X-Organization-ID: org_your_organization_id" \
    -H "Content-Type: application/json" \
    -d '{
      "message": "Find the five newest open issues in acme/api and summarize them, grouped by label.",
      "llm": {
        "integration_name": "openai",
        "provider_id": "openai",
        "model_id": "gpt-4o"
      }
    }'
  ```

  ```python Python theme={null}
  from modulex import AsyncModulex

  client = AsyncModulex(
      api_key="mx_live_your_api_key",
      organization_id="org_your_organization_id",
  )

  resp = await client.assistant.chat(
      "Find the five newest open issues in acme/api and summarize them, grouped by label.",
      llm={
          "integration_name": "openai",
          "provider_id": "openai",
          "model_id": "gpt-4o",
      },
  )
  print(resp.stream_url)  # open this to stream the turn
  ```

  ```javascript JavaScript theme={null}
  import { Modulex } from "modulex-js";

  const client = new Modulex({
    apiKey: "mx_live_your_api_key",
    organizationId: "org_your_organization_id",
  });

  const resp = await client.assistant.chat({
    message:
      "Find the five newest open issues in acme/api and summarize them, grouped by label.",
    llm: {
      integration_name: "openai",
      provider_id: "openai",
      model_id: "gpt-4o",
    },
  });
  console.log(resp.stream_url); // open this to stream the turn
  ```
</CodeGroup>

A successful response returns the chat and run identifiers plus a `stream_url`. Open it over Server-Sent Events to receive the Assistant's actions and output as they happen — including any [human-in-the-loop](/assistant/human-in-the-loop) approval pause, which you answer to continue:

```json Response theme={null}
{
  "status": "running",
  "chat_id": "f2b1c0de-1111-2222-3333-444455556666",
  "run_id": "a1b2c3d4-aaaa-bbbb-cccc-dddddddddddd",
  "thread_id": "f2b1c0de-1111-2222-3333-444455556666",
  "stream_url": "/assistant/chat/f2b1c0de-1111-2222-3333-444455556666/listen/a1b2c3d4-aaaa-bbbb-cccc-dddddddddddd"
}
```

For the full set of Assistant operations, the event stream, and the resume contract, see [how the Assistant works](/assistant/how-it-works), [streaming responses](/assistant/streaming), and [human-in-the-loop](/assistant/human-in-the-loop).

## Where to go next

<CardGroup cols={2}>
  <Card title="Assistant overview" icon="bot" href="/assistant/overview">
    What the Assistant is, what it can do, and how to start a session.
  </Card>

  <Card title="Recipes" icon="sparkles" href="/power-using/recipes">
    Reusable patterns that combine the Assistant with workflows and knowledge to solve real problems.
  </Card>

  <Card title="Using tools" icon="wrench" href="/assistant/using-tools">
    How the Assistant discovers and calls the integration tools these examples rely on.
  </Card>

  <Card title="Human-in-the-loop" icon="hand" href="/assistant/human-in-the-loop">
    The pause-and-approve step that protects every sensitive action above.
  </Card>

  <Card title="Connect an integration" icon="plug" href="/guides/connect-an-integration">
    Connect a service so the Assistant can act on it, then call its tools.
  </Card>

  <Card title="Build with the AI Composer" icon="wand-sparkles" href="/guides/build-with-composer">
    Turn a task you run often into a reusable, schedulable workflow.
  </Card>
</CardGroup>
