# Business systems

> Step-by-step connection and configuration for Atlassian, Stripe, Slack, Notion, Zendesk, ServiceNow, Linear, Intercom, HubSpot, Salesforce, Freshdesk, and PostHog, which the picker files here.

Source: https://triagic.com/docs/integrations/business

The systems that hold the human context around a ticket: the runbook, the past
conversation, the charge that was actually refunded.

> **Note:** PostHog is in this picker group, documented with observability
>
> The catalog lists **PostHog** under Business systems, but it reads product analytics,
> so its steps live with the rest of them: see
> [PostHog](/docs/integrations/observability#posthog).

## Atlassian [#atlassian]

`atlassian` · runs `mcp-atlassian` via **uvx** with read-only mode on, which disables
create, update and delete across both products.

Overview, prompts and the tool list: [/integrations/atlassian](/integrations/atlassian).

Confluence and Jira are configured independently. A product is enabled only when its
**URL** is filled in, so a Jira-only setup is a valid configuration. It just carries
fewer tools.

1. **Pick the authentication method your deployment can actually issue.** The
   **Authentication method** select changes which fields appear; the ones the other
   methods use are hidden, are not required, and are dropped when you save.

   * **Cloud — account email + API token.** A `*.atlassian.net` site. The default.
   * **Server / Data Center — personal access token.** Self-hosted. These instances
     cannot issue Atlassian API tokens at all, and take a bare bearer token with no email
     beside it.
   * **Cloud — pre-issued OAuth 2.0 access token.** For someone who already mints access
     tokens out of band.

2. **Create the credential.**

   **Cloud:** an API token at
   [id.atlassian.com → Security → API tokens](https://id.atlassian.com/manage-profile/security/api-tokens).
   One token works for both products. It acts as the Atlassian account it belongs to, so
   use an account whose Jira and Confluence permissions are read-only.

   **Server / Data Center:** a personal access token from the account's own profile,
   under **Profile → Personal Access Tokens → Create token**, in each product. Jira needs
   **8.14 or newer**, Confluence **6.0 or newer**; older versions have no such screen and
   cannot use this method. The token carries that account's permissions, so create it
   under a read-only account. There is no email field to fill in; a bare token is the
   whole credential.

   **Pre-issued OAuth token:** paste a token you already hold, plus the site's **cloud
   ID**. Read it from `api.atlassian.com/oauth/token/accessible-resources` called with
   that token. It is a UUID, not the site URL.

   > **Warning:** An OAuth access token expires, and nothing here renews it
   >
   > Triagic can't run the browser authorization flow that mints or refreshes one: there
   > is no browser to open and nowhere to receive the redirect. Atlassian access tokens
   > last about an hour, so this method suits an operator who is already issuing them, not
   > a connection meant to stay up.

3. **Fill in one or both product blocks.**

   | Field                                | Required                  | What to put                                                                                                                     |
   | ------------------------------------ | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
   | **Authentication method**            | yes, defaults Cloud       | Which of the three shapes above.                                                                                                |
   | **Confluence URL**                   | per product               | `https://your-company.atlassian.net/wiki` (note the `/wiki` suffix). Self-hosted looks like `https://wiki.acme.internal`.       |
   | **Confluence email**                 | Cloud, with the URL       | The Atlassian account email the token belongs to.                                                                               |
   | **Confluence API token**             | Cloud, with the URL       |                                                                                                                                 |
   | **Confluence personal access token** | Data Center, with the URL | From the account's profile. No email goes with it.                                                                              |
   | **Jira URL**                         | per product               | `https://your-company.atlassian.net`, or `https://jira.acme.internal`.                                                          |
   | **Jira email**                       | Cloud, with the URL       | The same account email.                                                                                                         |
   | **Jira API token**                   | Cloud, with the URL       | The same token is fine.                                                                                                         |
   | **Jira personal access token**       | Data Center, with the URL | From the account's profile.                                                                                                     |
   | **OAuth access token**               | OAuth only                | A token you already hold.                                                                                                       |
   | **OAuth cloud ID**                   | OAuth only                | The site's UUID, not its URL. One token and one cloud ID cover both products.                                                   |
   | **CA certificate path**              | no                        | Self-hosted behind a private CA. An absolute path on the member's machine to that CA's PEM bundle; it applies to both products. |
   | **Verify TLS certificate**           | no, defaults on           | Turn off only for a self-signed instance you cannot supply a CA bundle for. It goes off for both products together.             |

   Filling an email or token without its URL does nothing. The product stays off.

4. **Verify.** Start-checked only: every search tool requires a query, so there is no
   fixed call to health-check with.

| If it reports                                           | It usually means                                                                                                                                                                                                                                 |
| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `401` / `unauthorized`                                  | The method doesn't match the deployment. Cloud wants the Atlassian *account* email and an API token; a login password never works. Data Center wants a personal access token and no email at all. A pre-issued OAuth token has probably expired. |
| `CERTIFICATE_VERIFY_FAILED`, `SSLCertVerificationError` | The instance's certificate isn't trusted by that machine. Set **CA certificate path**, or turn off **Verify TLS certificate**.                                                                                                                   |

## Stripe [#stripe]

`stripe` · runs the official `@stripe/mcp` via **npx**, pinned to a version whose
`--tools` flag still validates: the spawned process is restricted to read tools
(balance, coupons, customers, disputes, documentation, invoices, payment intents,
prices, products, subscriptions), so the write half is refused by the process itself
as well as by your key.

Overview, prompts and the tool list: [/integrations/stripe](/integrations/stripe).

1. **Create a restricted key.** [dashboard.stripe.com/apikeys](https://dashboard.stripe.com/apikeys)
   → **Create restricted key**, granting **Read** on: Balance, Customers, Products,
   Prices, Invoices, Coupons, Payment intents, Subscriptions, Disputes. Nothing write.

   Mind the mode: a test-mode key can't see live data.

2. **Fill the form.**

   | Field       | Required | What to put                                                                            |
   | ----------- | -------- | -------------------------------------------------------------------------------------- |
   | **API key** | yes      | `rk_live_…` (restricted). A full secret key works but grants far more than this needs. |

3. **Verify.** Health check retrieves the account balance.

| If it reports                                   | It usually means                                   |
| ----------------------------------------------- | -------------------------------------------------- |
| `Invalid API key`                               | Truncated paste, or the wrong mode (test vs live). |
| `Restricted API key … insufficient permissions` | Add Read on the resource named in the error.       |

## Slack [#slack]

`slack` · runs `slack-mcp-server` via **npx** · posting is **off** by default: the
posting tool is not registered at all unless you enable it below.

Overview, prompts and the tool list: [/integrations/slack](/integrations/slack).

1. **Create a Slack app** at [api.slack.com/apps](https://api.slack.com/apps) → **From
   scratch**, in the workspace you want searchable.

2. **Add user token scopes** under **OAuth & Permissions → Scopes → User Token Scopes**:
   `search:read` (the reason this integration exists), plus `channels:history`,
   `channels:read`, `groups:history` / `groups:read` for private channels, `users:read`
   (looking up who posted), and `usergroups:read` (resolving `@team` mentions).

   If you intend to turn on **Allow posting messages** below, also add `chat:write`.
   Without it the posting tool is registered but every post fails with `missing_scope`.
   Leave it off the token for a read-only setup, so the scope list matches what the
   integration can actually do.

   > **Warning:** Use a user token, not a bot token
   >
   > Slack's `search.messages` API refuses bot tokens outright. A bot token (`xoxb-`) works
   > for reading history but **cannot search at all**.

   > **Warning:** One tool never works here, on any token
   >
   > The saved-items list ("Save for Later") needs Slack's undocumented browser-session
   > tokens, which this integration deliberately doesn't accept — no scope on an `xoxp` or
   > `xoxb` token fixes it. Treat a `not_allowed_token_type` on that one tool as expected,
   > not a misconfiguration.

3. **Install the app to the workspace** and copy the **User OAuth Token** (`xoxp-…`).
   Invite the token's user to any private channels you want readable.

4. **Fill the form.**

   | Field                         | Required         | What to put                                                                                                                                          |
   | ----------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
   | **Slack token**               | yes              | `xoxp-…`. The prefix is what selects the token type; you don't restate it.                                                                           |
   | **Allow posting messages**    | no, defaults off | Turn on to let Triagic post findings back to Slack, workspace-wide.                                                                                  |
   | **Limit posting to channels** | no               | Comma-separated channel IDs, e.g. `C0123456789,C9876543210`. Setting this enables posting **only** to those channels and overrides the toggle above. |
   | **CA certificate path**       | no               | Only when something re-signs the connection to slack.com. An absolute path on the member's machine to the inspecting proxy's CA bundle.              |
   | **Proxy URL**                 | no               | An outbound proxy to route through, e.g. `http://proxy.acme.internal:3128`. A `user:password@` prefix works.                                         |
   | **Verify TLS certificate**    | no, defaults on  | Turn off only if you cannot get the proxy's CA bundle.                                                                                               |

5. **If the connection is inspected on the way out,** the token isn't the problem, the
   certificate is. A corporate TLS-inspecting proxy re-signs slack.com with its own CA,
   which the server does not trust, so put an absolute path to that CA's PEM bundle in
   **CA certificate path** and the proxy's address in **Proxy URL**. Your network team
   has both.

   A password in the proxy URL is stored encrypted like any other field on this form, and
   is stripped from the connection target shown in the UI.

   > **Warning:** The CA path is the fix; the toggle is the last resort
   >
   > Turning off **Verify TLS certificate** makes the server accept *any* certificate
   > presented for slack.com, which is exactly what an interceptor needs. Trusting the one
   > CA leaves everything else verified.

6. **Verify.** Health check lists one public channel. On a very large workspace the first
   attempt can land before the server's channel cache is warm; it fails closed and
   recovers on the next sync.

| If it reports                                             | It usually means                                                                                                                                                                                                                                                    |
| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `invalid_auth`, `token_revoked`                           | Re-copy from OAuth & Permissions. Reinstalling the app invalidates the old token.                                                                                                                                                                                   |
| `missing_scope`, `not_allowed_token_type`                 | Searching needs a user token with `search:read`; history needs `channels:history` and `channels:read`; looking up users or `@team` mentions needs `users:read` / `usergroups:read`. On the saved-items tool specifically, this is expected — see the callout above. |
| `not_in_channel`                                          | Invite the token's user to that private channel.                                                                                                                                                                                                                    |
| A certificate or TLS error rather than a Slack error slug | Something is inspecting the connection to slack.com. Set **CA certificate path** and **Proxy URL**.                                                                                                                                                                 |

## Notion [#notion]

`notion` · runs the official `@notionhq/notion-mcp-server` via **npx**.

Overview, prompts and the tool list: [/integrations/notion](/integrations/notion).

> **Warning:** Read-only is the integration's capability, not a flag
>
> The server exposes write tools and has no switch to drop them. Granting the Notion
> integration &#x2A;*only "Read content"** when you create it makes every write fail at
> Notion's side. That's the real boundary, and it's set once, at creation.

1. **Create an internal integration** at
   [notion.so/profile/integrations](https://www.notion.so/profile/integrations). Under
   **Capabilities**, select **Read content** and nothing else.

2. **Copy the Internal Integration Secret** (`ntn_…`). The integration's own ID is not a
   token.

3. **Connect it to the pages you want readable.*&#x2A; In Notion, open the page (or the
   parent of a whole space), then &#x2A;*••• → Connections →** add the integration. Nothing
   is visible to it until you do this; sharing is per-page and it is the step people miss.

4. **Fill the form.**

   | Field                 | Required | What to put |
   | --------------------- | -------- | ----------- |
   | **Integration token** | yes      | `ntn_…`     |

5. **Verify.** Health check reads the integration's own bot user, so it fails exactly
   when the token is wrong. Note that it passes even when nothing has been shared yet.

| If it reports                          | It usually means                                                            |
| -------------------------------------- | --------------------------------------------------------------------------- |
| `unauthorized`, `API token is invalid` | Wrong string: copy the Internal Integration Secret, not the integration id. |
| `object_not_found`                     | Almost always step 3: the page isn't connected to the integration.          |
| `restricted_resource`                  | Capabilities were set to "No content".                                      |

## Zendesk [#zendesk]

`zendesk` · runs `zendesk-mcp` via **npx**.

Overview, prompts and the tool list: [/integrations/zendesk](/integrations/zendesk).

> **Warning:** Writes are not blocked here
>
> The server exposes ticket and user writes and offers no read-only flag. A Zendesk API
> token acts as the agent whose email is paired with it, so **the role of that agent is
> the boundary**. Mint the token against a read-only custom role or a Light Agent.

1. **Enable API token access.** Admin Center → **Apps and integrations → Zendesk API**,
   turn on token access, then **Add API token**. Copy it; it's shown once.

2. **Note the agent email** the token was created under. The two are paired; a mismatch
   is a 401.

3. **Fill the form.**

   | Field           | Required | What to put                                                                    |
   | --------------- | -------- | ------------------------------------------------------------------------------ |
   | **Subdomain**   | yes      | Just the subdomain: for `https://acme.zendesk.com` that's `acme`, not the URL. |
   | **Agent email** | yes      | The account the token was created under.                                       |
   | **API token**   | yes      |                                                                                |

4. **Verify.** Health check lists organizations, which exercises all three fields.

| If it reports                       | It usually means                                                        |
| ----------------------------------- | ----------------------------------------------------------------------- |
| `ENOTFOUND` / `getaddrinfo`         | The subdomain field has a full URL in it.                               |
| `401` / `Couldn't authenticate you` | Email and token don't match, or API token access is still switched off. |
| `403`                               | End users can't use the API at all. The token must belong to an agent.  |

## ServiceNow [#servicenow]

`servicenow` · runs `servicenow-mcp-ai` via **npx** · read-only twice over: the
server's own read-only switch refuses every create, update and delete, and its write
mode is pinned to *plan*, which returns a non-mutating preview instead of acting.

Overview, prompts and the tool list: [/integrations/servicenow](/integrations/servicenow).

1. **Pick the authentication method the instance can issue.** The **Authentication
   method** select changes which fields appear; the ones the other methods use are
   hidden, are not required, and are dropped when you save. Read-only is enforced on
   Triagic's side under all four.

   * **Basic — username + password.** The default, and the one every instance supports.
   * **OAuth 2.0 — client credentials.** The token belongs to a registered application
     rather than to a person.
   * **Inbound API key.** A key scoped by an inbound authentication profile.
   * **Pre-issued bearer token.** A token you already hold. Nothing here refreshes it.

2. **Create the credential.**

   **Basic:** a service account with **REST API access** and a read-only role. `itil` is
   enough to read incidents and changes; `snc_read_only` or a custom read role is the
   least-privilege option. The account must not be locked out, and must not be an
   interactive-only user; instances often block those from the REST API.

   **OAuth:** **System OAuth → Application Registry → Create an OAuth API endpoint for
   external clients**. Copy the **Client ID** and **Client Secret** from the record.
   Triagic uses the *client credentials* grant, the only one of ServiceNow's four that
   runs without a browser. So grant the application itself read-only access, because
   there is no user behind the token whose roles would limit it.

   **Inbound API key:** **System Web Services → API Key**. It goes out as the
   `x-sn-apikey` header and reaches only the endpoints its inbound authentication profile
   allows, so the profile is where you narrow it.

   **Bearer token:** paste one you already hold.

   > **Warning:** What is deliberately not offered
   >
   > The password (ROPC) grant is deprecated by ServiceNow. The refresh-token grant needs
   > the browser flow that issued the refresh token, which Triagic cannot run. The
   > JWT-bearer grant and mutual-TLS client certificates both need a PEM private key
   > pasted into a form, a different class of secret than a token, and not something this
   > app handles today.

3. **Fill the form.**

   | Field                      | Required           | What to put                                                                                             |
   | -------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------- |
   | **Instance URL**           | yes                | The full `https://acme.service-now.com` form.                                                           |
   | **Authentication method**  | no, defaults Basic | Which of the four shapes above.                                                                         |
   | **Username**               | Basic              | The service account.                                                                                    |
   | **Password**               | Basic              |                                                                                                         |
   | **OAuth client ID**        | OAuth              | From the Application Registry record.                                                                   |
   | **OAuth client secret**    | OAuth              | From the same record.                                                                                   |
   | **API key**                | API key            | From System Web Services → API Key.                                                                     |
   | **Bearer token**           | Bearer token       | A token you already hold.                                                                               |
   | **CA certificate path**    | no                 | On-prem instance behind a private CA. An absolute path on the member's machine to that CA's PEM bundle. |
   | **Verify TLS certificate** | no, defaults on    | Turn off only for a self-signed instance you cannot supply a CA bundle for.                             |

4. **Verify.** Health check lists tables, which fails on a bad URL, a rejected
   credential, or a user without table visibility.

| If it reports                                                                    | It usually means                                                                                                                       |
| -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `401` / `User Not Authenticated`                                                 | Wrong credentials, a locked-out account, or one without web-service access.                                                            |
| `403` / `ACL`                                                                    | Signed in, but an ACL blocked the read. Grant a role that can read the tables you need.                                                |
| `ENOTFOUND`, `hibernating`                                                       | Wrong URL, or a personal developer instance that needs waking from the developer portal.                                               |
| `unable to get local issuer`, `SELF_SIGNED_CERT…`, `DEPTH_ZERO_SELF_SIGNED_CERT` | An on-prem instance whose certificate this machine doesn't trust. Set **CA certificate path**, or turn off **Verify TLS certificate**. |

## Linear [#linear]

`linear` · connects to Linear's hosted server at `https://mcp.linear.app/mcp/readonly`.

Overview, prompts and the tool list: [/integrations/linear](/integrations/linear).

> **Note:** Read-only twice over
>
> Triagic always uses Linear's `/mcp/readonly` endpoint, which only lists read tools.
> Make the API key Read only as well, so it can't write even if it's reused somewhere
> else.

1. **Create a personal API key.** In Linear, open **Settings → Account → Security &
   access → New API key**. Set the permission to **Read** and, if you want, limit it to
   the teams the agent should see. Copy it; it's shown once.

2. **Fill the form.**

   | Field       | Required | What to put                   |
   | ----------- | -------- | ----------------------------- |
   | **API key** | yes      | The key, starting `lin_api_`. |

3. **Verify.** Health check lists teams, which fails on a bad or revoked key.

| If it reports                | It usually means                                                                            |
| ---------------------------- | ------------------------------------------------------------------------------------------- |
| `401` / `invalid_token`      | The key is wrong or revoked, or it's an OAuth client secret rather than a personal API key. |
| `403` / `Forbidden`          | The key is limited to other teams, or an admin has turned off member API keys.              |
| `ENOTFOUND` / `fetch failed` | This machine can't reach `mcp.linear.app`. Check the network or proxy.                      |

## Intercom [#intercom]

`intercom` · connects to Intercom's hosted server at `https://mcp.intercom.com/mcp`
(US) or `https://mcp.eu.intercom.com/mcp` (EU).

Overview, prompts and the tool list: [/integrations/intercom](/integrations/intercom).

> **Warning:** AU workspaces aren't supported yet
>
> Intercom's MCP server only runs for US and EU hosted workspaces. An AU workspace
> can't connect until Intercom adds it.

1. **Get an access token.** In Intercom's **Developer Hub**, open or create an app for
   your workspace, then **Configure → Authentication**. Edit its permissions down to
   **Read conversations**, **Read and list users and companies**, and article read if
   you want Help Center search. Copy the access token.

2. **Fill the form.**

   | Field                | Required | What to put                                                  |
   | -------------------- | -------- | ------------------------------------------------------------ |
   | **Workspace region** | no       | US (default) or EU, matching where your workspace is hosted. |
   | **Access token**     | yes      | The token from the app's Authentication page.                |

3. **Verify.** Health check lists companies, which fails on a bad token or the wrong region.

Creating and updating articles and adding internal notes are left out, and so is
the tool that sends feedback to Intercom.

| If it reports                  | It usually means                                                               |
| ------------------------------ | ------------------------------------------------------------------------------ |
| `Access Token Invalid` / `401` | The token is wrong, or the region doesn't match the workspace.                 |
| `403` / `forbidden`            | The app is missing a read permission. Add it under Configure → Authentication. |
| `ENOTFOUND` / `fetch failed`   | This machine can't reach Intercom's MCP endpoint. Check the network or proxy.  |

## HubSpot [#hubspot]

`hubspot` · runs `@hubspot/mcp-server` via **npx**.

Overview, prompts and the tool list: [/integrations/hubspot](/integrations/hubspot).

> **Warning:** Writes are left out, not blocked
>
> The server has create and update tools for records, properties and engagements, and
> no read-only flag. Triagic only exposes the list, search and get tools, and refuses
> any object type that isn't a plain name, since the server builds request paths from
> it. Give the key read scopes only so it can't write if it's ever used elsewhere.

1. **Create a Service Key.** In HubSpot, open **Settings → Integrations → Service Keys**
   and create one with read scopes: `crm.objects.contacts.read`,
   `crm.objects.companies.read`, `crm.objects.deals.read`. Add `tickets` only if you want
   ticket lookups; HubSpot's tickets scope also allows writes. An existing private app's
   access token (`pat-…`) works too, but HubSpot is retiring new private apps.

2. **Fill the form.**

   | Field            | Required | What to put                                  |
   | ---------------- | -------- | -------------------------------------------- |
   | **Access token** | yes      | The Service Key or private app access token. |

3. **Verify.** Health check lists one contact, so the key needs `crm.objects.contacts.read`.

| If it reports                    | It usually means                                                                          |
| -------------------------------- | ----------------------------------------------------------------------------------------- |
| `401` / `INVALID_AUTHENTICATION` | The token is wrong or rotated out. A developer API key or OAuth client secret won't work. |
| `403` / `MISSING_SCOPES`         | The key lacks the read scope for that object. Add it and try again.                       |

## Salesforce [#salesforce]

`salesforce` · runs `@tsmztech/mcp-server-salesforce` via **npx**.

Overview, prompts and the tool list: [/integrations/salesforce](/integrations/salesforce).

Triagic gets SOQL queries, SOSL search and object describes, so it can read cases,
accounts, contacts and custom objects. The record, metadata and Apex tools the server
also ships aren't exposed. SOQL can't write, so this holds even for a user with full
access. The user's profile still decides which records and fields come back.

1. **Create the app.** Setup → **External Client App Manager** → **New External Client App**
   (a Connected App works the same). Turn on OAuth, add the `api` scope, and enable the
   **Client Credentials Flow**.

2. **Pick the run-as user.** In the app's policies, set **Run As** to an integration user
   with **API Enabled** and read-only access to the objects you want triaged. Every query
   runs as this user.

3. **Copy the consumer key and secret** from the app's OAuth settings. New apps can take a
   few minutes before the first token request succeeds.

4. **Fill the form.**

   | Field                                  | Required                    | What to put                                                                            |
   | -------------------------------------- | --------------------------- | -------------------------------------------------------------------------------------- |
   | **Instance URL**                       | yes                         | Your My Domain URL, like `https://acme.my.salesforce.com`. Not `login.salesforce.com`. |
   | **Authentication method**              | no                          | Client credentials (default), or username + password + security token.                 |
   | **Consumer key** / **Consumer secret** | yes, for client credentials | From the app above.                                                                    |
   | **Username** / **Password**            | yes, for password           | An integration user, not an admin.                                                     |
   | **Security token**                     | no                          | Needed unless this machine's IP is in the org's trusted ranges.                        |

5. **Verify.** Health check searches the object list for `Case`, which needs a working login
   and nothing else.

| If it reports                          | It usually means                                                          |
| -------------------------------------- | ------------------------------------------------------------------------- |
| `request not supported on this domain` | Client credentials against `login.salesforce.com`. Use the My Domain URL. |
| `no client credentials user enabled`   | The app has no Run As user set.                                           |
| `invalid_client`                       | Wrong consumer key or secret, or the app is still propagating.            |
| `INVALID_LOGIN`                        | Wrong password or security token, or the user is locked out.              |
| `API_DISABLED_FOR_ORG`                 | The user lacks API Enabled, or the edition has no API access.             |

## Freshdesk [#freshdesk]

`freshdesk` · runs `freshdesk-mcp` via **npx**.

Overview, prompts and the tool list: [/integrations/freshdesk](/integrations/freshdesk).

> **Warning:** The agent's role is the second boundary
>
> The server ships ticket, contact and knowledge base writes with no read-only flag.
> Triagic exposes only the tools that send GET requests, and a Freshdesk API key acts as
> the agent it belongs to, so **use a key from an agent whose role can view but not reply
> or edit**.

1. **Pick the agent.** Ideally a dedicated agent with a custom role limited to viewing
   tickets, contacts and solutions (custom roles need the Pro plan or above).

2. **Copy its API key.** Signed in as that agent: profile picture → **Profile settings** →
   **View API key**.

3. **Fill the form.**

   | Field               | Required | What to put                                                       |
   | ------------------- | -------- | ----------------------------------------------------------------- |
   | **Helpdesk domain** | yes      | `acme.freshdesk.com`, or just `acme`. Not a custom portal domain. |
   | **API key**         | yes      |                                                                   |

4. **Verify.** Health check reads the current agent (`/agents/me`), which fails on a wrong
   domain or key.

| If it reports               | It usually means                                                    |
| --------------------------- | ------------------------------------------------------------------- |
| `ENOTFOUND` / `getaddrinfo` | The domain is a custom portal address or has a typo.                |
| `HTTP 401`                  | The key is wrong or belongs to another helpdesk.                    |
| `HTTP 403`                  | The agent's role can't view that resource.                          |
| `HTTP 429`                  | The account's per-minute API limit, shared with other integrations. |
