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

# Slack Triage Bot

> Automate legal request triage in Slack with a Zapier workflow, no code required.

export const WebhookTestUrl = ({showJobId}) => {
  const [url, setUrl] = useState('');
  const [jobId, setJobId] = useState('');
  const [status, setStatus] = useState('idle');
  var jobUrl = showJobId ? 'https://app.gc.ai/api/external/v1/jobs/' + jobId.trim() : 'https://app.gc.ai/api/external/v1/jobs/test-id';
  var qs = '?job_url=' + encodeURIComponent(jobUrl) + '&attempt_number=1&channel=C0123ABCDEF&message-ts=1234567890.123456';
  var full = url.trim().replace(/\/+$/, '') + qs;
  var ready = url.trim().length > 0 && (!showJobId || jobId.trim().length > 0);
  var busy = status === 'sending';
  var send = function () {
    if (!ready || busy) return;
    setStatus('sending');
    fetch(full, {
      mode: 'no-cors'
    }).then(function () {
      setStatus('sent');
    }).catch(function () {
      setStatus('error');
    });
  };
  var label = status === 'sending' ? 'Sending...' : status === 'sent' ? 'Sent!' : status === 'error' ? 'Failed' : 'Send test request';
  var inputStyle = {
    flex: 1,
    padding: '8px 12px',
    border: '1px solid #e2e8f0',
    borderRadius: '6px',
    fontSize: '14px',
    background: 'transparent',
    color: 'inherit'
  };
  var jobIdStyle = {
    flex: 'none',
    width: '280px',
    padding: '8px 12px',
    border: '1px solid #e2e8f0',
    borderRadius: '6px',
    fontSize: '14px',
    background: 'transparent',
    color: 'inherit'
  };
  return <div style={{
    display: 'flex',
    gap: '8px',
    margin: '8px 0 16px'
  }}>
      <input type="text" value={url} onChange={function (e) {
    setUrl(e.target.value);
    setStatus('idle');
  }} placeholder="Paste your Catch Hook webhook URL" style={inputStyle} />
      {showJobId ? <input type="text" value={jobId} onChange={function (e) {
    setJobId(e.target.value);
    setStatus('idle');
  }} placeholder="Paste Job Id" style={jobIdStyle} /> : null}
      <button onClick={send} disabled={!ready || busy} style={{
    padding: '8px 16px',
    borderRadius: '6px',
    border: 'none',
    fontSize: '14px',
    fontWeight: 500,
    cursor: ready && !busy ? 'pointer' : 'not-allowed',
    opacity: ready && !busy ? 1 : 0.4,
    background: status === 'sent' ? '#22c55e' : '#6366f1',
    color: '#fff',
    whiteSpace: 'nowrap'
  }}>
        {label}
      </button>
    </div>;
};

<Info>**No code required.** This tutorial uses Zapier's point-and-click builder. No programming experience needed.</Info>

Set up a Zapier automation that watches a Slack channel for legal requests, sends them to GC AI for classification, and replies in-thread with a structured triage report.

<Frame>
  <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-result.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=47bba81c2ad298e53b9d273f51bcde40" alt="Slack thread showing a legal request with an automated triage reply including category, risk level, urgency, and recommended next steps" width="2800" height="1999" data-path="images/demo-slack-bot-result.png" />
</Frame>

## What you'll build

A Zapier "Zap" that monitors a Slack channel (e.g. `#legal-requests`). When someone posts a message describing a legal need, the automation:

1. Sends the message to GC AI for classification
2. Replies in the same Slack thread with a structured triage: category, risk level, urgency, a summary, recommended next steps, and which playbook to use in GC AI

## API endpoints

| Method | Endpoint                                                        | What it does                                                            |
| ------ | --------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `POST` | [`/v1/chat/completions`](/api-reference/create-chat-completion) | Classifies the request                                                  |
| `GET`  | [`/v1/jobs/:id`](/api-reference/get-async-job)                  | Polls for the result when the classification takes longer than expected |

## Prerequisites

* A [GC AI API key](/api-reference/introduction#authentication) (get one from **Settings > API** in the app)
* A [Zapier](https://zapier.com) account (the free plan works for testing; a paid plan is needed for multi-step Zaps to run automatically)
* Admin access to a Slack workspace

## Part 1: The basic Zap

<Steps>
  <Step title="Create a Slack channel">
    Create a dedicated channel for incoming legal requests, something like `#legal-requests`. This is the channel the bot will watch.

    If your team already has a channel for this purpose, you can use that instead.
  </Step>

  <Step title="Create a new Zap">
    Go to [zapier.com](https://zapier.com) and click **Create** → **New Zap**. You'll see an empty canvas with a trigger step.
  </Step>

  <Step title="Set up the Slack trigger">
    Click the trigger step and configure it:

    1. **App**: Search for **Slack** and select it
    2. **Event**: Choose **New Message Posted to Channel**
    3. **Account**: Connect your Slack workspace (Zapier will walk you through the OAuth flow)
    4. **Channel**: Select your `#legal-requests` channel
    5. **Trigger on bot messages**: Set to **No** (this prevents the bot from responding to its own replies)
    6. Click **Test trigger** to pull in a sample message

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-step1-01.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=9ce006325349ca09407e5a88be080a1e" alt="Zapier Slack trigger setup: choosing the app and event" width="2800" height="1999" data-path="images/demo-slack-bot-step1-01.png" />
    </Frame>

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-step1-02.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=2a0e240e9c01e8fcc9558ab748325c98" alt="Zapier Slack trigger configuration: channel selection and bot message filter" width="2800" height="1999" data-path="images/demo-slack-bot-step1-02.png" />
    </Frame>

    <Tip>Post a test message in the channel before testing the trigger so Zapier has sample data to work with.</Tip>
  </Step>

  <Step title="Send a 'working on it' reply">
    Add a new step: **Slack** → **Send Channel Message**.

    | Field            | Value                                                                               |
    | ---------------- | ----------------------------------------------------------------------------------- |
    | **Channel**      | Select `#legal-requests` (or map the **Channel ID** from the trigger)               |
    | **Message Text** | `Working on it...`                                                                  |
    | **Thread**       | Map the **Ts** field from the Slack trigger (this makes the reply appear in-thread) |
    | **Bot Name**     | `GC AI Triage` (optional)                                                           |

    This gives the requester instant feedback while the API processes their request. We'll update this message with the actual result in a later step.

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-step3-01.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=2fb0bf3d7682e3c27708e6a8d01dd8a8" alt="Zapier Slack Send Channel Message setup: choosing the app and event" width="2800" height="1999" data-path="images/demo-slack-bot-step3-01.png" />
    </Frame>

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-step3-02.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=aff8447163ee541390d7a281c74b493d" alt="Zapier Slack Send Channel Message configuration: channel, message text, and bot name" width="2800" height="1999" data-path="images/demo-slack-bot-step3-02.png" />
    </Frame>

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-step3-03.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=e72c76c26988c8a098bf656e1cbe420e" alt="Zapier Slack Send Channel Message configuration continued: thread Ts mapping" width="2800" height="1999" data-path="images/demo-slack-bot-step3-03.png" />
    </Frame>
  </Step>

  <Step title="Classify with GC AI">
    Add a new step: **Webhooks by Zapier** → **POST**.

    | Field                     | Value                                                        |
    | ------------------------- | ------------------------------------------------------------ |
    | **URL**                   | `https://app.gc.ai/api/external/v1/chat/completions?wait=30` |
    | **Payload Type**          | `Json`                                                       |
    | **Data**                  | Key: `message` / Value: see below                            |
    | **Wrap Request In Array** | `No`                                                         |
    | **Unflatten**             | `Yes`                                                        |
    | **Headers**               | Key: `Authorization` / Value: `YOUR_API_KEY`                 |

    For the **Data** value, paste the following prompt. Replace `{{insert Message Text}}` with the mapped field from your Slack trigger (click the **+** button in the value field, then select **Message Text** from the Slack trigger data):

    ```
    You are a legal operations triage assistant. A team member posted the following request in Slack:

    "{{insert Message Text}}"

    Classify this request and respond with:

    Category: [Contract Review, Employment, IP, Compliance, Litigation, Corporate, or Other]
    Risk Level: [Low, Medium, High, or Critical]
    Urgency: [Routine, Expedited, or Urgent]

    Summary: [One sentence describing the request]

    Recommended Next Steps:
    1. [First step]
    2. [Second step]
    3. [Third step]

    Suggested GC AI Playbook: [Which playbook type to use]

    Keep the response concise. Use bold (*text*) for labels.
    ```

    The `?wait=30` on the URL tells the API to hold the connection for up to 30 seconds while it works, comfortably inside Zapier's 30-second limit for action steps, so the step returns cleanly instead of erroring out. (The API reads `wait` from the query string or a `Prefer: wait=30` header, not the JSON body, so it must live on the URL here.) Click **Test step**. Quick classifications come back with `status: "succeeded"` and the text inside `result` → `result`. If the request needs longer than 30 seconds, the call returns with a non-`succeeded` status and a `job_id`; the next step handles that case (and Part 2 polls the job to completion).

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-step4-01.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=a130d180013eeea169be96d5a8474e72" alt="Zapier Webhooks by Zapier setup: choosing the app and POST event" width="2800" height="1999" data-path="images/demo-slack-bot-step4-01.png" />
    </Frame>

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-step4-02.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=7e0e9925b30260f7c578f91986e8f7e2" alt="Zapier Webhooks POST configuration: URL, payload type, data key with classification prompt" width="2800" height="1999" data-path="images/demo-slack-bot-step4-02.png" />
    </Frame>

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-step4-03.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=e83fec265f15bd511f2faedc1adbdb7a" alt="Zapier Webhooks POST configuration continued: wrap in array, unflatten, headers with authorization" width="2800" height="1999" data-path="images/demo-slack-bot-step4-03.png" />
    </Frame>
  </Step>

  <Step title="Branch on the result">
    Add a new step: **Paths by Zapier**.

    The API usually finishes within the wait window, but longer queries can time out. This step handles both cases.

    **Path A: Classification ready**

    | Field         | Value                                                         |
    | ------------- | ------------------------------------------------------------- |
    | **Condition** | `Status` (from the Webhooks step) exactly matches `succeeded` |

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-step6-01.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=720bd08b6dc3c4b0b436587d5aab8e24" alt="Zapier Path A conditions: status exactly matches succeeded" width="2800" height="1999" data-path="images/demo-slack-bot-step6-01.png" />
    </Frame>

    Add a **Slack** → **Edit Message** step inside this path. When searching for the action, choose the first "Edit Message" option (the one with just "Edits a message." as the description).

    | Field               | Value                                                                                                                  |
    | ------------------- | ---------------------------------------------------------------------------------------------------------------------- |
    | **Conversation ID** | Your `#legal-requests` channel                                                                                         |
    | **Message TS (ID)** | Map the **Ts** from the "Working on it..." step (this identifies which message to update)                              |
    | **Message Content** | Map the **Result Result** field from the Webhooks step (the AI's classification text, found under `result` → `result`) |
    | **Content Type**    | `Markdown`                                                                                                             |

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-step7-01.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=2a7df76dabf2baf607e58718962829e1" alt="Zapier Edit Message setup: choosing the first Edit Message option" width="2800" height="1999" data-path="images/demo-slack-bot-step7-01.png" />
    </Frame>

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-step7-02.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=7cba258b89e631eee4751128f5f5ef81" alt="Zapier Edit Message configuration: conversation ID, message TS, and message content mapped from the webhooks result" width="2800" height="1999" data-path="images/demo-slack-bot-step7-02.png" />
    </Frame>

    **Path B: Timed out**

    | Field         | Value                                                                |
    | ------------- | -------------------------------------------------------------------- |
    | **Condition** | `Status` (from the Webhooks step) does not exactly match `succeeded` |

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-step8-01.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=56bf65245817c74c20bc7148be6f573d" alt="Zapier Path B conditions: status does not exactly match succeeded" width="2800" height="1999" data-path="images/demo-slack-bot-step8-01.png" />
    </Frame>

    Add a **Slack** → **Edit Message** step inside this path (same first option):

    | Field               | Value                                                                                      |
    | ------------------- | ------------------------------------------------------------------------------------------ |
    | **Conversation ID** | Your `#legal-requests` channel                                                             |
    | **Message TS (ID)** | Map the **Ts** from the "Working on it..." step                                            |
    | **Message Content** | `This request timed out. It may be too complex for the bot. Try asking in GC AI directly.` |
    | **Content Type**    | `Markdown`                                                                                 |

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-step9-01.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=d241d82554583a25094db7267a2512ca" alt="Zapier Edit Message setup for the failure path" width="2800" height="1999" data-path="images/demo-slack-bot-step9-01.png" />
    </Frame>

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-step9-02.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=bd6afded6d678479367ec529a6c31acd" alt="Zapier Edit Message configuration for the failure path: conversation ID, message TS, and error message content" width="2800" height="1999" data-path="images/demo-slack-bot-step9-02.png" />
    </Frame>
  </Step>

  <Step title="Test and publish">
    1. Post a test message in `#legal-requests`:

    > We just signed a new vendor and need their MSA reviewed before onboarding. It's a 3-year SaaS agreement with auto-renewal and an uncapped indemnification clause. Timeline is end of week.

    2. Click **Test** on each step to run the Zap manually, or click **Publish** and post the message to see it run automatically.

    3. You should see a threaded reply with the structured triage.

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-result.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=47bba81c2ad298e53b9d273f51bcde40" alt="The completed Zap showing all steps: Slack trigger, working on it reply, Webhooks POST to GC AI, and Paths with success and failure branches" width="2800" height="1999" data-path="images/demo-slack-bot-result.png" />
    </Frame>

    Once it's working, click **Publish** to turn the Zap on. Every new message in the channel will now be triaged automatically.
  </Step>
</Steps>

### Exercise: trigger the bot only on certain messages

Right now the bot triages every message posted in the channel. How would you configure it so the bot only runs when someone specifically addresses it, for example by starting their message with "hey legalbot"?

<Accordion title="Hint">
  Add a **Filter by Zapier** step between the Slack trigger and the Webhooks POST. Set the condition to only continue if **Message Text** contains `hey legalbot`. Every other message in the channel will be ignored.

  <Frame>
    <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-step2-01.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=e2172096e16eeb64d9a32e164f25e99f" alt="Zapier Filter by Zapier step configured to only continue if message text contains hey legalbot" width="2800" height="1999" data-path="images/demo-slack-bot-step2-01.png" />
  </Frame>
</Accordion>

## Part 2: Handling long-running queries

The basic Zap works well for quick classifications, but some queries, especially those involving large documents or complex analysis, can take longer than the API's wait window. When that happens, Path B fires and the user sees "Your request is taking longer than expected."

To actually follow up with the result, we'll build a **second Zap** that polls for the completed job. The main Zap calls this sub-Zap when a query times out, and the sub-Zap keeps checking until the result is ready.

### How it works

The sub-Zap receives a `job_url`, an `attempt_number`, and the Slack context needed to update the "Working on it..." message. It waits, checks the job status, and then:

* **Job succeeded** → updates the "Working on it..." message with the result
* **Max attempts reached** → updates the message with an error
* **Still running** → calls itself again with `attempt_number + 1`

This is recursion: the sub-Zap calls itself with an incremented counter until it either gets a result or hits the retry limit.

### Build the polling sub-Zap

<Steps>
  <Step title="Create a new Zap with a webhook trigger">
    Create a second Zap. For the trigger, choose **Webhooks by Zapier** → **Catch Hook**.

    In the **Configure** tab, leave **Pick off a Child Key** empty and click **Continue**.

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-p2-step1-01.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=77891a9eab9e549fed31c6ad63b68d3f" alt="Zapier Catch Hook Configure tab: Pick off a Child Key left empty" width="2442" height="2000" data-path="images/demo-slack-bot-p2-step1-01.png" />
    </Frame>

    Zapier will give you a unique webhook URL (something like `https://hooks.zapier.com/hooks/catch/123456/abcdef/`). Copy this URL; the main Zap will call it.

    In the **Test** tab, click **Test trigger**. Zapier will wait for a request. To send test data, you'll need a real Job Id. Go to your main Zap, open the Webhooks POST step, and copy the **Job Id** from its test results.

    Then paste both values below and click **Send test request**:

    <WebhookTestUrl showJobId={true} />

    Go back to Zapier. It should pick up the request and show the four fields: `job_url`, `attempt_number`, `channel`, and `message-ts`. Once the request appears, select the record and click **Continue with selected record**.

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-p2-step1-02.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=c15061c57218eb0ac14db8c0249de741" alt="Zapier Catch Hook Test tab: test data received showing the four fields" width="2442" height="2000" data-path="images/demo-slack-bot-p2-step1-02.png" />
    </Frame>
  </Step>

  <Step title="Check the job status">
    Add a step: **Webhooks by Zapier** → **GET**.

    | Field                   | Value                                        |
    | ----------------------- | -------------------------------------------- |
    | **URL**                 | Map `job_url` from the Catch Hook trigger    |
    | **Send As JSON**        | `Yes`                                        |
    | **Unflatten**           | `Yes`                                        |
    | **Query String Params** | Key: `wait` / Value: `30`                    |
    | **Headers**             | Key: `Authorization` / Value: `YOUR_API_KEY` |

    The `wait` parameter makes this GET long-poll the job for up to 30 seconds, so each attempt gives GC AI real processing time before the path branches. It also paces the recursion: there's no need for a separate Delay step, since every loop spends up to 30 seconds here before deciding whether to try again.

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-p2-step2-01.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=bb31b239505f8c977533a78144dc6432" alt="Zapier Webhooks GET configuration: URL mapped to job_url, headers with authorization" width="2442" height="2000" data-path="images/demo-slack-bot-p2-step2-01.png" />
    </Frame>
  </Step>

  <Step title="Branch on the result">
    Add a step: **Paths by Zapier** with three paths.

    **Path A: Job succeeded**

    | Field         | Value                                           |
    | ------------- | ----------------------------------------------- |
    | **Condition** | `Status` (from the GET step) equals `succeeded` |

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-p2-step3-01.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=230edd1ad8774144c44f3eaa2ad8f4bf" alt="Zapier Path A conditions: status equals succeeded" width="2442" height="2000" data-path="images/demo-slack-bot-p2-step3-01.png" />
    </Frame>

    Add a **Slack** → **Edit Message** step (the first option):

    | Field               | Value                                        |
    | ------------------- | -------------------------------------------- |
    | **Conversation ID** | Map `channel` from the Catch Hook trigger    |
    | **Message TS (ID)** | Map `message-ts` from the Catch Hook trigger |
    | **Message Content** | Map **Result Result** from the GET step      |
    | **Content Type**    | `Markdown`                                   |

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-p2-step3-02.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=faf660570227372bd2e43cb6100aa17d" alt="Zapier Path A Edit Message configuration: channel, message TS, and result mapped from GET step" width="2442" height="2000" data-path="images/demo-slack-bot-p2-step3-02.png" />
    </Frame>

    **Path B: Max attempts reached**

    Without this path, a job that never completes would recurse forever. This is the safety valve: after 4 attempts, stop and tell the user.

    | Field           | Value                                                                          |
    | --------------- | ------------------------------------------------------------------------------ |
    | **Condition 1** | `Status` (from the GET step) does not equal `succeeded`                        |
    | **Condition 2** | `Attempt Number` (from the Catch Hook trigger) is greater than or equal to `4` |

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-p2-step3-03.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=acc012685e24d1085b339f2b434b92c3" alt="Zapier Path B conditions: status does not equal succeeded and attempt number greater than or equal to 4" width="2442" height="2000" data-path="images/demo-slack-bot-p2-step3-03.png" />
    </Frame>

    Add a **Slack** → **Edit Message** step (the first option):

    | Field               | Value                                                                                    |
    | ------------------- | ---------------------------------------------------------------------------------------- |
    | **Conversation ID** | Map `channel` from the Catch Hook trigger                                                |
    | **Message TS (ID)** | Map `message-ts` from the Catch Hook trigger                                             |
    | **Message Content** | `Sorry, this request is taking too long to process. Please try again in GC AI directly.` |
    | **Content Type**    | `Markdown`                                                                               |

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-p2-step3-04.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=d58d509eb5ec002a74105bd55352df6f" alt="Zapier Path B Edit Message configuration: error message for max attempts reached" width="2442" height="2000" data-path="images/demo-slack-bot-p2-step3-04.png" />
    </Frame>

    **Path C: Try again (recurse)**

    | Field           | Value                                                           |
    | --------------- | --------------------------------------------------------------- |
    | **Condition 1** | `Status` (from the GET step) does not equal `succeeded`         |
    | **Condition 2** | `Attempt Number` (from the Catch Hook trigger) is less than `4` |

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-p2-step3-05.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=71b682911faad65e71582241e0d13fbf" alt="Zapier Path C conditions: status does not equal succeeded and attempt number less than 4" width="2442" height="2000" data-path="images/demo-slack-bot-p2-step3-05.png" />
    </Frame>

    First, add a **Formatter by Zapier** → **Numbers** step:

    | Field         | Value                                                    |
    | ------------- | -------------------------------------------------------- |
    | **Transform** | `Perform Math Operation`                                 |
    | **Operation** | `Add`                                                    |
    | **Values**    | `1` and map `attempt_number` from the Catch Hook trigger |

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-p2-step3-06.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=2d575cad56af7d959b5928fec7708d9e" alt="Zapier Formatter step: Perform Math Operation, Add, with 1 and attempt_number as values" width="2442" height="2000" data-path="images/demo-slack-bot-p2-step3-06.png" />
    </Frame>

    Then add a **Webhooks by Zapier** → **GET** step that calls the sub-Zap's own webhook URL:

    | Field                   | Value                                                        |
    | ----------------------- | ------------------------------------------------------------ |
    | **URL**                 | Your sub-Zap's Catch Hook URL (the one from Step 1)          |
    | **Query String Params** | `job_url`: map from Catch Hook trigger                       |
    |                         | `attempt_number`: map the **Output** from the Formatter step |
    |                         | `channel`: map from Catch Hook trigger                       |
    |                         | `message-ts`: map from Catch Hook trigger                    |

    <Frame>
      <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-p2-step3-07.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=a5d70288c2b8d4f35fb3dfbb3a6c1501" alt="Zapier recursive GET step: URL set to sub-Zap's own Catch Hook URL with query string params" width="2442" height="2000" data-path="images/demo-slack-bot-p2-step3-07.png" />
    </Frame>
  </Step>
</Steps>

### Wire the sub-Zap into the main Zap

Go back to your main Zap and update **Path B** (the timed-out branch). Replace the **Edit Message** step with a **Webhooks by Zapier** → **GET** step (keep the Edit Message that says "This request timed out..."; the sub-Zap will update it again when the result is ready):

| Field                   | Value                                                                                                                                   |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **URL**                 | Your sub-Zap's Catch Hook URL                                                                                                           |
| **Query String Params** | `job_url`: type `https://app.gc.ai/api/external/v1/jobs/` then map **Job Id** from the main Zap's Webhooks step (it appends to the URL) |
|                         | `attempt_number`: `1`                                                                                                                   |
|                         | `channel`: map **Channel ID** from the Slack trigger                                                                                    |
|                         | `message-ts`: map **Ts** from the "Working on it..." step                                                                               |

<Frame>
  <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-p2-wire-01.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=5220c3ef863bd28e14dc20709077249a" alt="Main Zap Path B updated with GET step to sub-Zap: URL and query string params configured" width="1854" height="1994" data-path="images/demo-slack-bot-p2-wire-01.png" />
</Frame>

Now when a classification times out, the main Zap updates the message to say it's still working and kicks off the polling sub-Zap. The sub-Zap polls (up to 4 times) and updates the same message with the result when it's ready.

<Frame>
  <img src="https://mintcdn.com/gcai/PDDupZNWNlKAYZ3B/images/demo-slack-bot-p2-result.png?fit=max&auto=format&n=PDDupZNWNlKAYZ3B&q=85&s=ea9456d9b868faa2be02b5dab0e9b287" alt="The completed sub-Zap showing all steps: Catch Hook trigger, GET job status, and Paths with success, max attempts, and recurse branches" width="2442" height="2000" data-path="images/demo-slack-bot-p2-result.png" />
</Frame>

## Customizing the prompt

The classification prompt in Step 4 is fully customizable. Here are some ideas:

* **Add your team's categories**: replace the generic list with your actual practice areas
* **Include routing rules**: tell the model which attorney or team handles which category
* **Reference your playbooks by name**: if you know your GC AI playbooks, list them in the prompt so the model suggests a specific one
* **Change the output format**: ask for a numbered priority score, a deadline estimate, or a link to the relevant GC AI playbook page

## Going further

<CardGroup cols={2}>
  <Card title="Add file analysis" icon="file-contract">
    Upload attachments from Slack to GC AI using the [file upload endpoint](/api-reference/upload-file), wait for processing, then include the file in the classification prompt via `file_ids`. Reuse the polling sub-Zap pattern to wait for file processing.
  </Card>

  <Card title="Run a playbook automatically" icon="list-check">
    After classification, use the [run playbook endpoint](/api-reference/run-playbook) to execute a full playbook review against the attached document. Post the check results back to Slack.
  </Card>

  <Card title="Route to the right person" icon="user-check">
    Add a lookup table in Zapier that maps categories to Slack users. After classification, `@mention` the right attorney in the thread reply so they know to pick it up.
  </Card>

  <Card title="Log to a spreadsheet" icon="table">
    Add a Google Sheets step to log every triage (timestamp, requester, category, risk, urgency) for reporting and SLA tracking.
  </Card>
</CardGroup>
