gentic

Gentic Surveys — Documentation

Connect any AI agent to your Fairing post-purchase surveys. Sync your zero-party survey data — every "how did you hear about us?" answer joined to the order it came from — into your organization's Brain, then ask in natural language: 'which channel actually drives revenue?', 'what do customers say vs what Meta claims?', 'which survey answers correlate with the highest AOV?'. Bounded, resumable syncs keep it current without re-reading everything.

1. Getting Started

Sign Up & Get Your API Key

Before you can use Gentic Surveys, you need an API key to authenticate your requests.

  1. Go to gentic.co/surveys and create an account.
  2. Create an organization from your dashboard. API keys and billing are scoped to the organization.
  3. 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/surveys. For Claude Code:

claude mcp add gentic-surveys \
  --transport http \
  https://mcp.gentic.co/surveys \
  --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 Surveys 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/surveys/SKILL.md

Or upload a .skill bundle to Claude Managed Agents:

https://gentic.co/surveys/gentic-surveys.skill

Download 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

4 tools, rendered live from the Gentic MCP manifest. Parameter tables come directly from each tool's JSON Schema.

fairing_connection_status

Free

Check whether the calling organization has connected Fairing (the post-purchase survey app). Returns `{ connected, shop_domain, updated_at }` when connected, or `{ connected: false }` otherwise. Free. Call before fairing_sync to give the user actionable guidance when the integration is missing. Never returns the API key.

This tool takes no parameters.

fairing_sync

Free

Sync post-purchase survey data from Fairing into this organization's data backend — READ-ONLY, verbatim, one BOUNDED chunk per call. Uses the org's connected Fairing integration (dashboard → Integrations; no API key is passed here). Warehouses two resources into the vendor-neutral per-org tables `survey_responses` and `survey_questions` (deduped on source + the provider's id, so re-syncs refresh rows in place). Each response row carries the customer's self-reported answer (e.g. "how did you hear about us") ALONGSIDE the order's real revenue (`order_total`, in `order_currency_code`) and its measured UTM trail — so self-reported vs measured attribution is a single-table query, no join. BOUNDED + RESUMABLE: each call pages only until `max_pages_per_run` pages OR `max_seconds_per_run` seconds are reached, then returns `has_more` + `next_cursor: { resource, starting_after }`. TWO LOOPS: (1) BACKFILL — call repeatedly passing `next_cursor` back as `cursor` until `has_more` is false, omitting `since` so the whole history is walked oldest-first. (2) ONGOING — on each scheduled run pass the `latest_inserted_at` returned by the previous run as `since`, and page with `cursor` until `has_more` is false. Responses are always fetched oldest-first, so a backfill converges on rows created while it runs. Returns `{ synced: { <resource>: count }, has_more, next_cursor, latest_inserted_at, persisted }`. ALWAYS CHECK `persisted`: false means a page failed to write, the run stopped there, and `next_cursor` has been rewound to retry that exact page — so re-calling with it is safe, but a run that keeps returning `persisted: false` with `synced: 0` is making no progress and needs a look at the logs, not another call. FREE.

ParameterTypeDescription
resourcesstring[]

Subset of resources to sync this run (in order). Any of: responses, questions. Omit to sync both.

sincestring

ISO 8601 date/timestamp — pull only responses inserted at or after this point (Fairing's incremental filter). On a scheduled sync, pass the `latest_inserted_at` returned by the previous run; that row is re-fetched and upserted as a no-op, which is what keeps the boundary lossless. OMIT it for a backfill — a `since` on the first walk permanently skips everything older.

page_sizeinteger

Rows per Fairing page (1–1000, default 250). Fairing caps this at 1000; larger pages are cheaper against the 100 req/min limit.

1 – 1000

max_pages_per_runinteger

Cap on pages fetched this call (default 20, hard max 50). Keeps a single call bounded — page more via next_cursor, never a bigger run.

1 – 50

max_seconds_per_runinteger

Wall-clock budget in seconds for this call (default 20, hard max 30). The walk stops after the current page once exceeded; resume with next_cursor.

1 – 30

cursorany

Resume point from a prior call's `next_cursor` — { resource, starting_after }. Omit/null to start from the first requested resource at the beginning.

hark_connection_status

Free

Check whether this organization has Hark (sendhark.com) connected and reachable. Makes one live read against the Hark API. Returns `{ connected, checked_live, total_harks, message }`. Call this when `hark_sync_harks` has failed, to tell a configuration problem apart from an empty result. Never returns the API token. Read-only. FREE.

This tool takes no parameters.

hark_sync_harks

Free

Sync Voice-of-Customer submissions from Hark (sendhark.com) into this organization's `voice_of_customer` table (`source='hark'`) — READ-ONLY, one BOUNDED chunk per call. A "hark" is a video or text submission a customer left, optionally with an AI sentiment, a transcript and a linked helpdesk ticket; NPS responses arrive as text harks on the `nps-survey` workflow. Uses the org's connected Hark integration (dashboard → Integrations; no token is passed here). HOW TO WALK IT. The first call FREEZES a window ceiling and returns it inside `next_cursor`; keep passing `next_cursor` back as `cursor` until `has_more` is false. That freeze is what makes the walk correct — Hark pages by offset over a collection that grows at the head, so an open-ended walk would silently skip rows as new submissions arrived. For a scheduled sync, pass the previous run's `latest_created_at` as `since` and start a fresh walk. Optional `hark_type`, `workflow` and `sentiment` filters narrow the pull; a cursor is refused if replayed with different filters or a different `since`, rather than resuming into a different collection. WHAT IS STORED. Rows are deduped on Hark's submission id and refreshed in place on re-sync. PRIVACY: the warehouse copy keeps `email` as the join key and DROPS name, phone and address entirely — they are stripped before storage and are not recoverable from this table; use `permalink` to open the original in Hark when a human needs the full record. Note that `submission_text` and `transcript` are free text a customer wrote or spoke, so they can still contain personal details nobody chose to store. Video and thumbnail links are deliberately not stored: Hark issues them as short-lived pre-signed URLs that stop working, so `permalink` is the stable reference. Returns `{ synced, total_in_window, has_more, next_cursor, latest_created_at, window, persisted, pii_fields_dropped }`. ALWAYS CHECK `persisted`: false means the page failed to write and the cursor was rewound to retry it. FREE.

ParameterTypeDescription
sincestring

ISO 8601 date — only submissions created at or after this point. On a scheduled sync pass the previous run's `latest_created_at`. OMIT for a full backfill; a `since` on the first walk permanently skips everything older.

hark_typestring

Optional filter, e.g. 'video' or 'text'.

workflowstring

Optional workflow filter. NPS submissions use the 'nps-survey' workflow; the numeric score itself lives on the per-submission detail record, not on this list, so it is not synced by this tool.

sentimentstring

Optional AI sentiment filter, e.g. 'positive' or 'negative'.

page_sizeinteger

Rows requested per Hark page (1–500, default 100). Hark's own server-side maximum is undocumented, so the walk uses the number of rows actually returned rather than the number requested.

1 – 500

max_pages_per_runinteger

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_runinteger

Wall-clock budget in seconds for this call (default 20, hard max 30).

1 – 30

cursorstring

Resume token — pass a prior call's `next_cursor` verbatim. It carries the frozen window and the filters it was produced with, and is refused rather than reinterpreted if either differs.

5. Pricing

Pricing is pulled live from the Gentic MCP manifest. All prices are per call and deducted from your Gentic credits.

ToolCost
fairing_connection_statusFree
fairing_syncFree
hark_connection_statusFree
hark_sync_harksFree

6. Notes

  • Organization-scoped: the Fairing connection comes from the org's connected integration (dashboard → Integrations → Fairing). No credential is ever passed to the tools.
  • Cost: both `fairing_connection_status` and `fairing_sync` are **free**. You pay only for analytics you run over the synced data (e.g. `query_data`). Pricing is pulled live from the Gentic MCP manifest.
  • `fairing_sync` is READ-ONLY — it warehouses Fairing data into your Brain and never writes back to Fairing.
  • Rows are deduped on `source` + the provider's id: re-syncing refreshes the same row (mutable fields and all) rather than duplicating it.
  • Backfill vs incremental: omit `since` for the first full walk (page the bounded `next_cursor` as `cursor` until `has_more` is false); pass `since` = your stored `latest_inserted_at` only for scheduled refreshes. A `since` on the FIRST run permanently skips older responses.
  • The two tables — `survey_responses` and `survey_questions` — are queried with `query_data` for all counts, aggregations, and said-vs-measured attribution.

7. When to Apply

  • User wants to know how customers say they heard about the brand ("how did you hear about us?").
  • User wants to reconcile self-reported attribution against measured attribution (Meta/Google/UTM) — said vs measured.
  • User wants to find which channels drive the most revenue or highest AOV by survey answer.
  • User wants to sync their Fairing post-purchase survey data into their Brain for analysis.
  • User wants to count or aggregate survey responses by question, answer, channel, or time.
  • User wants to feed survey attribution into a digest, marketing-mix review, or spend-allocation decision.

8. Workflow

  1. 1. Connect Fairing once, then sync without credentials

    The Surveys server sources the Fairing connection (the Secret Token) from the org's connected integration (Gentic dashboard → Integrations → Fairing). You never pass credentials to the tools — connect once in the dashboard and the server reads the encrypted connection automatically. Call `fairing_connection_status` first (it's free) to confirm the org is connected; if it returns `{ connected: false }`, point the user to Integrations → Fairing. It never returns the API key.

  2. 2. Sync survey data into the Brain with `fairing_sync`

    `fairing_sync` is **free**. It is READ-ONLY and warehouses two resources into vendor-neutral per-org tables — `survey_responses` and `survey_questions` — deduped on `source` + the provider's id, so a re-sync refreshes existing rows in place rather than duplicating them. Each response row carries the customer's self-reported answer ALONGSIDE the order's real revenue and its measured UTM trail. Returns `{ synced: { <resource>: count }, has_more, next_cursor, persisted, latest_inserted_at }` — store `latest_inserted_at` as your watermark for the next incremental run.

  3. 3. First, do a full BACKFILL — omit `since`

    For the initial load, call `fairing_sync` with NO `since` argument. Each call is BOUNDED: it pages only until `max_pages_per_run` pages OR `max_seconds_per_run` seconds, then returns `has_more: true` and a `next_cursor: { resource, starting_after }`. Call again passing that `next_cursor` back as `cursor` — repeat until `has_more` is false. That walks the whole store, oldest-first. ⚠️ Do NOT pass `since` on the first run: `since` is Fairing's incremental filter, so a `since` on the initial backfill permanently skips every response older than that timestamp — you can't get them back without re-walking from scratch.

  4. 4. Then keep it fresh with an incremental `since` watermark

    For scheduled refreshes AFTER the backfill is complete, pass `since` set to the high-water mark from your last run so you pull only new responses instead of re-walking the whole store. Store the `latest_inserted_at` the sync reports and pass it back as `since` next time. If a run reports `latest_inserted_at: null` (no new rows seen), keep your previous watermark rather than overwriting it. Between the bounded-cursor backfill and the `since` watermark, you never re-read data you already have and never miss a new response.

  5. 5. Reconcile said-vs-measured with `query_data`

    The survey tools warehouse data; they don't analyze it. For attribution reconciliation — which channels customers credit vs what Meta/Google/UTM claimed, revenue by survey answer, AOV by "how did you hear?" — use `query_data` (Gentic Data MCP) over `survey_responses` and `survey_questions`. Because each response already carries the answer, the revenue, and the UTM trail in one row, said-vs-measured is a single-table GROUP BY — no join to a separate orders table required. Sync first (or re-sync incrementally) so the numbers are current.

  6. 6. Present results clearly

    Don't dump raw JSON. Summarize the attribution picture — top self-reported channels, where customers and the pixels disagree, revenue and AOV by answer — and cite the counts. After a sync, tell the user how many rows were `persisted` per resource and whether `has_more` is true (there's more to page through). For recurring checks, re-run `fairing_sync` with your `since` watermark to refresh before querying.