Gentic Support — Documentation
Connect any AI agent to your customer support. Sync your Gorgias tickets — full message threads and all — and your Crisp live-chat history into your organization's Brain, then search them together in natural language: 'what are customers complaining about?', 'what themes are we seeing this week?', 'find tickets about shipping delays'. Add your Loop Returns data — every return and returned item, with its return reason — to ask 'which SKUs get returned most, and why?' and check it against what the same customers told support. Incremental syncs keep the picture current without re-reading the whole backlog.
1. Getting Started
Sign Up & Get Your API Key
Before you can use Gentic Support, you need an API key to authenticate your requests.
- Go to gentic.co/support and create an account.
- Create an organization from your dashboard. API keys and billing are scoped to the organization.
- Generate an API key and use it as a Bearer token in your MCP client.
2. Connecting to the MCP Server
The server is available at https://mcp.gentic.co/support. For Claude Code:
claude mcp add gentic-support \
--transport http \
https://mcp.gentic.co/support \
--header "Authorization: Bearer YOUR_API_KEY"For Claude Web and ChatGPT you can also connect via OAuth — no API key needed. See the connect section on the landing page for other MCP clients (n8n, OpenClaw).
3. Agent Skill
For the best results, pair the MCP server with the Gentic Support agent skill. The MCP server gives your agent tool access; the skill teaches it the optimal workflow order. Both the raw SKILL.md and a ready-to-upload .skill bundle are generated on demand from the live manifest, so they always reflect the current tools and pricing.
Add the skill directly via URL:
https://gentic.co/support/SKILL.mdOr upload a .skill bundle to Claude Managed Agents:
https://gentic.co/support/gentic-support.skillDownload this file and upload it wherever Claude Managed Agents asks for a .skill file. It's a zip bundle generated on demand from the latest SKILL.md.
4. Tool Reference
7 tools, rendered live from the Gentic MCP manifest. Parameter tables come directly from each tool's JSON Schema.
crisp_connection_status
Check whether this organization's Crisp live-chat workspace is connected and reachable, before importing chat history. Returns the connected workspace's name and domain so you can confirm it is the right one. Free.
| Parameter | Type | Description |
|---|---|---|
_noop | string | Unused. This tool takes no arguments. |
crisp_sync_conversations
Pull Crisp live-chat data into this organization's warehouse: imports historical Crisp chat conversations and transcripts into the support tickets table, so past chat history is searchable alongside tickets from other help desks. Walks the archive newest-first in bounded chunks: each call returns `next_cursor`, and you call again with that cursor until `has_more` is false. Safe to call repeatedly — re-importing a conversation refreshes it rather than duplicating it. If the provider rate-limits the import it stops cleanly and resumes from the same point on the next call, so nothing is lost and nothing is re-charged. Costs 1 cent per conversation imported.
| Parameter | Type | Description |
|---|---|---|
cursor | string | Resume token from a previous call's `next_cursor`. Omit on the first call to start a fresh import from the most recent conversations. Pass it back verbatim — it is opaque and must not be constructed or edited. |
limit | integer | Conversations to import in this call (default 50, max 50). 1 – 50 |
max_seconds | integer | Wall-clock budget in seconds (default 20, max 45). The import stops after the conversation in flight once exceeded and returns a cursor. 1 – 45 |
gorgias_sync_status
Check the status of an async gorgias_sync_tickets chunk by its `job_id`. Returns `{ status: 'processing' | 'completed' | 'failed' }`, and when completed the chunk result: `{ count, next_cursor, has_more, latest_updated_datetime, timed_out }`. Use this when you didn't pass a `webhook_url` to gorgias_sync_tickets: poll until status is 'completed', then save `next_cursor` and call gorgias_sync_tickets again with it until `has_more` is false. Free.
| Parameter | Type | Description |
|---|---|---|
job_idrequired | string | The job_id returned by a gorgias_sync_tickets call. |
gorgias_sync_tickets
Sync support tickets from Gorgias into this organization's Brain (the per-org `support_tickets` table) — ASYNC, one chunk per call. Uses the org's connected Gorgias integration (dashboard → Integrations); no API key is passed here. Returns immediately with `{ job_id, status: 'processing' }`; the actual work (pull a page of tickets, fetch messages, embed, upsert deduped on the Gorgias ticket id) runs off-box and the chunk result — INCLUDING `next_cursor`, `has_more`, `latest_updated_datetime`, `count` — is delivered when done. Provide `webhook_url` to have that result POSTed to you (the recommended flow for n8n: save `next_cursor` from the webhook and call again with it until `has_more` is false); without a `webhook_url`, poll `gorgias_sync_status` with the returned `job_id`. Two modes via `mode`: 'incremental' (default) pulls NEWEST-FIRST — right for 'the most recent N tickets' (`limit: N`, no `updated_since`), for a date-bounded backfill (`updated_since: '2025-01-01T00:00:00Z'`, then page with `next_cursor`), and for ongoing polling (pass back `updated_since`); 'backfill' walks OLDEST-FIRST through full history via `cursor`. In incremental mode, if `updated_since` is more than 7 days older than the newest ticket already synced, the call still runs but the ack and the webhook result carry `watermark_stale: true` and `suggested_updated_since`: an `updated_since` that never advances re-delivers the whole history on every call, and every re-delivered ticket is billed. Billed 1¢ per ticket actually synced (charged when the chunk completes, not at dispatch).
| Parameter | Type | Description |
|---|---|---|
limit | integer | Max tickets to sync in this chunk (1–100, default 25). Each ticket incurs a messages fetch + an embedding and requests are rate-limit-paced, so the worker chunks; page with `next_cursor` for more. 1 – 100 |
mode | string | 'incremental' (default): newest-first; with no `updated_since` returns the most-recent `limit` tickets, with `updated_since` returns only tickets changed since (paged newest→oldest to the watermark). 'backfill': oldest-first walk through ALL history via `cursor`. enum: incremental, backfill |
updated_since | string | INCREMENTAL mode only. High-water mark (ISO-8601). Only tickets with `updated_datetime` strictly newer than this are synced. Omit for the first/full pull. When a poll finds nothing new, the returned `latest_updated_datetime` may be null — keep your PREVIOUS watermark in that case. |
cursor | string | Resume cursor — pass a prior chunk's `next_cursor` to continue the same walk (the primary control for backfill, and for paging a large incremental pull). Omit to start a fresh walk. |
webhook_url | string | Where to POST the chunk result when it completes: `{ job_id, status, count, next_cursor, has_more, latest_updated_datetime, timed_out, table }`. The recommended n8n flow — your webhook receives this, saves `next_cursor`, and fires the next call with it until `has_more` is false. If omitted, poll `gorgias_sync_status` with the `job_id` instead. |
loop_returns_connection_status
Check whether the calling organization has connected Loop Returns (loopreturns.com — the returns and exchanges app, not the subscriptions one). Returns `{ connected, updated_at, tenant: null, tenant_note }`, or `{ connected: false, hint }`. Never returns the API key. Set check_live to verify the stored key with one read: `problem` tells apart `key_not_recognized` (generate a new key) from `scope_missing` (add the Returns scope in Loop's Developer Tools; no new key needed).
| Parameter | Type | Description |
|---|---|---|
check_live | boolean | When true, make one minimal read against Loop Returns to verify the stored key works and carries the Returns scope. One request. |
loop_returns_sync
Sync returns from Loop Returns (loopreturns.com — the returns/exchanges app, not the subscriptions one) into this organization's warehouse — READ-ONLY, one BOUNDED chunk per call, using the org's connected integration (no API key or shop argument). Writes `loop_returns` (one row per return: state, outcome, totals, order, customer email) and `loop_return_line_items` (one row per returned item: sku, product, return_reason, parent_return_reason, return_comment, outcome), upserted so a re-sync refreshes rows in place. For return reasons, most-returned SKUs, the exchange-vs-refund mix, and joins to support tickets (customer email) and Shopify orders (provider_order_id). Call again with `next_cursor` (and the same `states`) until `has_more` is false. Walks all five states — open, closed, expired, cancelled, review. Use start_from:'warehouse' on a schedule and 'beginning' (default) for the first backfill. `persisted: false` means a page failed to save: re-call with `next_cursor`, which points at that page. A non-empty `stalled` means a state stopped because the upstream cursor did not advance: that needs a look, not another call.
| Parameter | Type | Description |
|---|---|---|
states | string[] | Subset of return states to sync this run, in order. Any of: open, closed, expired, cancelled, review. Omit to sync all five (recommended — Loop's own default leaves out cancelled and review). |
start_from | string | "beginning" (default) walks each state from `since`. "warehouse" starts each state where its last complete walk ended (a state never completed starts from `since`) — use it on a schedule. A `cursor` always wins for the state it names, because it resumes a run already in flight. enum: beginning, warehouse |
since | string | ISO date/time the walk starts from when a state has no warehouse start (default: 730 days ago). Matched against updated_at. |
page_size | integer | Returns per Loop page (1–750, default 250). 1 – 750 |
max_pages_per_run | integer | Cap on pages fetched this call (default 20, hard max 50). Page more via next_cursor, never via a bigger run. 1 – 50 |
max_seconds_per_run | integer | Wall-clock budget in seconds (default 60, hard max 120). The walk stops before the next page once exceeded; resume with next_cursor. 1 – 120 |
cursor | any | Resume point from a prior call's `next_cursor` — { state, from, to, cursor, page_size }, passed back unchanged. Omit or null to start from the first requested state. |
search_support_tickets
Search this organization's support tickets by natural language — the right tool for 'what are customers complaining about regarding <product>?', 'what themes are we seeing this week?', 'find tickets about shipping delays'. Searches the `support_tickets` table (populated by gorgias_sync_tickets) semantically over each ticket's conversation text, returning full ticket rows ranked by relevance. Supports an optional SQL `filters` clause over the structured columns to narrow results — e.g. status, channel, customer_email, ticket_created_at/ticket_updated_at (TIMESTAMP), tags (JSON). Example: search_support_tickets(query='damaged on arrival', filters="status = 'open' AND ticket_created_at > '2026-06-01'"). For pure counts/aggregations (group by status, ticket volume per day), use query_data over the support_tickets table instead.
| Parameter | Type | Description |
|---|---|---|
queryrequired | string | Natural-language search query, e.g. 'shipping delays and damaged packaging' or 'complaints about the new subscription flow'. |
filters | string | Optional SQL filter clause (the body of a WHERE) over structured columns: status, channel, via, customer_email, customer_name, ticket_created_at, ticket_updated_at, message_count, csat_score. Read-only; write/file functions are rejected. Example: "status = 'open' AND ticket_updated_at > '2026-06-01'". |
limit | integer | Number of tickets to return (1–50, default 10). 1 – 50 |
5. Pricing
Pricing is pulled live from the Gentic MCP manifest. All prices are per call and deducted from your Gentic credits.
| Tool | Cost |
|---|---|
| crisp_connection_status | Free |
| crisp_sync_conversations | 1¢ / result |
| gorgias_sync_status | Free |
| gorgias_sync_tickets | 1¢ / result |
| loop_returns_connection_status | Free |
| loop_returns_sync | Free |
| search_support_tickets | Free |
6. Notes
- Organization-scoped: the Gorgias, Crisp and Loop Returns connections come from the org's connected integrations (dashboard → Integrations). No credential is ever passed to the tools.
- Loop Returns needs an API key with the **Returns** scope. Loop accepts a key without it, but that key can't read returns — `loop_returns_connection_status` with `check_live: true` reports `scope_missing` for it and `key_not_recognized` for a key Loop rejects.
- Loop Returns is not Loop Subscriptions: the returns tools are `loop_returns_*` here on `/support`; the subscription tools live on `/subscriptions`.
- Cost: `gorgias_sync_tickets` is **1¢ per synced ticket**; `crisp_sync_conversations` is **1¢ per conversation imported**; `search_support_tickets`, `gorgias_sync_status`, `crisp_connection_status`, `loop_returns_sync` and `loop_returns_connection_status` are **free**. Pricing is pulled live from the Gentic MCP manifest.
- `search_support_tickets` only sees tickets already synced into `support_tickets` — run `gorgias_sync_tickets` first, and re-run incrementally to keep results current.
- Tickets are deduped on the Gorgias ticket id: re-syncing an updated ticket refreshes the same row (mutable fields and all) rather than duplicating it.
- For scheduled polling, persist `latest_updated_datetime` and pass it back as `updated_since` — that's the supported incremental pattern.
- Use `search_support_tickets` for 'find/what about' questions and `query_data` over `support_tickets` for counts and aggregations.
7. When to Apply
- User wants to know what customers are complaining about, or the top themes in recent tickets.
- User wants to find support tickets about a specific issue, product, or topic.
- User wants to sync their Gorgias tickets into their Brain for search and analysis.
- User wants to import their Crisp live-chat history so past chats are searchable alongside their tickets.
- User wants to poll for new/updated tickets on a schedule without re-reading the whole backlog.
- User wants to count or aggregate tickets — volume per day, breakdown by status or channel.
- User wants to feed recent support conversations into a digest, triage flow, or product feedback loop.
- User wants to know which products or SKUs get returned most, the top return reasons, or the exchange-vs-refund mix.
- User wants to sync their Loop Returns data into their Brain for analysis.
- User wants to compare returns with the support tickets or Shopify orders of the same customers.
8. Workflow
1. Connect Gorgias once, then sync without credentials
The Support server sources the Gorgias connection (domain, email, API key) from the org's connected integration (Gentic dashboard → Integrations → Gorgias). You never pass credentials to the tools — connect once in the dashboard and the server reads the encrypted connection automatically. If `gorgias_sync_tickets` reports no connection, point the user to Integrations → Gorgias.
2. Import Crisp chat history with `crisp_sync_conversations`
If the org uses Crisp, check it first with `crisp_connection_status` (free) — it returns the connected workspace's name and domain so you can confirm it's the right one; if it isn't connected, point the user to Integrations → Crisp. `crisp_sync_conversations` is **1¢ per conversation imported**. It imports chat conversations and transcripts into the same `support_tickets` table as Gorgias, so `search_support_tickets` covers both. It walks the archive newest-first in bounded chunks: call again with the returned `next_cursor` until `has_more` is false. Re-importing a conversation refreshes it rather than duplicating it, and if Crisp rate-limits the import it stops cleanly and resumes from the same point on the next call — nothing is lost or re-charged.
3. Connect Loop Returns once — with the Returns scope
Loop Returns data comes from the org's Loop Returns integration (Gentic dashboard → Integrations → Loop Returns): an API key generated in Loop Admin → Developer Tools. The key must carry the **Returns** scope — Loop accepts a key without it, but that key can't read any returns. Check with `loop_returns_connection_status` (free) and `check_live: true`: if `problem` is `scope_missing`, tell the user to click the scopes next to the key in Loop's Developer Tools and add Returns (no new key needed); if it is `key_not_recognized`, they need to generate a new key and reconnect it under Integrations → Loop Returns. Loop doesn't say which shop a key belongs to, so no shop name comes back — don't go looking for one. Loop Returns (loopreturns.com, returns and exchanges) is a different company from Loop Subscriptions, whose tools live on `/subscriptions`.
4. Sync returns with `loop_returns_sync`
`loop_returns_sync` is **free** and read-only. It writes two tables: `loop_returns` (one row per return — state, outcome, totals, order, customer email) and `loop_return_line_items` (one row per returned item — sku, product, `return_reason`, `parent_return_reason`, `return_comment`, outcome), upserted so a re-sync refreshes rows in place. Each call is one bounded chunk and walks all five return states (open, closed, expired, cancelled, review) unless you pass `states`. When `has_more` is true, call again with the returned `next_cursor`, unchanged, and the same `states`. Use `start_from: 'beginning'` (the default) for the first backfill and `start_from: 'warehouse'` on a schedule, so each state resumes where its last complete walk ended. `persisted: false` means a page failed to save — re-call with `next_cursor`, which points at that page. A non-empty `stalled` means a state stopped because Loop's cursor didn't advance: report it to the user rather than calling again.
5. Answer return questions with `query_data`
Return questions are line-item grained — reason × SKU — so answer them with `query_data` over `loop_return_line_items` (`sku`, `title`, `return_reason`, `outcome`), joined to `loop_returns` on `return_id` = `loop_returns.id` when you need the order or the customer. Join `loop_returns.customer_email` to `support_tickets.customer_email` to see what the same customers told support, and `provider_order_id` to Shopify orders.
6. Sync tickets into the Brain with `gorgias_sync_tickets`
`gorgias_sync_tickets` is **1¢ per synced ticket**. It pulls tickets updated-ascending with cursor pagination, fetches each ticket's full message thread, and upserts into the per-org `support_tickets` table deduped on the Gorgias ticket id — so a ticket update refreshes the same row rather than creating a duplicate. `limit` (1–100, default 25) caps how many tickets this call gathers; keep batches modest for interactive use since each ticket also incurs a messages fetch and an embedding. Returns `{ tickets, count, latest_updated_datetime, next_cursor, has_more, persisted, embedded }`.
7. Poll incrementally with `updated_since` / `latest_updated_datetime`
For scheduled or repeat runs, persist the `latest_updated_datetime` returned by each sync and pass it back as `updated_since` on the next run to fetch only tickets changed since the last check. Use `cursor` (a prior call's `next_cursor`) to continue a large backfill in the same session; `has_more` tells you whether another page remains.
8. Search tickets in natural language with `search_support_tickets`
`search_support_tickets` is **free**. It searches the `support_tickets` table semantically over each ticket's conversation text and returns full ticket rows ranked by relevance — the right tool for 'what are customers complaining about regarding <product>?' or 'find tickets about shipping delays'. Searches only what's already been synced, so run `gorgias_sync_tickets` first if the data may be stale.
9. Narrow with the SQL `filters` clause
Pass an optional SQL `filters` clause over the structured columns to scope a search — e.g. `status`, `channel`, `customer_email`, `ticket_created_at` / `ticket_updated_at` (TIMESTAMP), `tags` (JSON). Example: `search_support_tickets(query='damaged on arrival', filters="status = 'open' AND ticket_created_at > '2026-06-01'")`.
10. Count and aggregate with `query_data`
For pure counts or aggregations — ticket volume per day, a breakdown by status or channel — use `query_data` (Gentic Data MCP) over the `support_tickets` table rather than semantic search. Semantic search ranks by relevance; `query_data` is for exact GROUP BY / COUNT analytics over the same rows.
11. Present results clearly
Don't dump raw JSON. Summarize the tickets — subject, customer, status, and the gist of the conversation — and surface the `count`. After a sync, tell the user how many tickets were `persisted` and `embedded`. For recurring checks, remind the user you'll track `latest_updated_datetime` so the next run only pulls new and updated tickets. For returns, lead with the answer — top SKUs, top reasons, the exchange-vs-refund mix — and say so if the sync stopped with `has_more` still true, since the numbers then cover only part of the history.