Build a connector
A connector indexes content from a source Gety doesn't cover by default. This guide takes you from the sample project to a connector installed in Gety. For the full field and API reference, see Connector reference.
Know the current scope
A connector is a Deno and TypeScript program that polls a source and yields documents for Gety to index.
It can:
- Pull data from a stable source it can access, such as a web API, local enumeration, or internal service
- Index text content with stable IDs, titles, and metadata
- Use persisted state to track incremental progress and add, update, or delete documents
It can't:
- Add its own UI to Gety beyond manifest-declared configuration fields
- Fetch, extract, or cache remote files on demand when a user opens a result
- Use a per-document dynamic link rule; document links are declared once in the manifest
If the source only exposes files that Gety would need to fetch and extract later, put the text your connector already has into content and store a stable URL or file path in metadata.
For example, Notion is a good connector fit: use the Notion API as the stable source, convert Notion blocks to Markdown with a library such as souvikinator/notion-to-md, then send that Markdown as the indexed content in Gety.
A media player is usually not a good connector fit. It is mainly an interactive UI, not a stable text source. Connectors also work poorly for sources that don't expose a reliable API, stable local files, or durable IDs for incremental sync.
Prerequisites
Before you start, install:
Create a connector with an AI agent
If your AI coding tool supports Skills, use Gety's connector skill first. It gives the agent the connector contract, sample project workflow, and verification steps.
npx skills add gety-ai/gety-skills --skills create-gety-connector
Then tell the agent what you want to index:
Use the create-gety-connector skill to create a Gety connector for [source].
Ask me for any missing details before you start coding.
If your AI coding tool doesn't support Skills, paste this into the chat instead:
Create a Gety connector for [source]. Use https://docs.gety.ai/build-a-connector.md as the implementation workflow and https://docs.gety.ai/connector-reference.md as the full connector contract and field reference. Before writing code, ask me to clarify any missing details.
Don't paste API keys or other secrets into a chat. Put secrets in connector configuration fields or local .env files.
If you're building by hand, follow the steps below.
Start from the sample
Clone the sample connector. Don't build the skeleton from scratch; the sample includes the build script, generated config types, local runner, and verification tasks.
git clone https://github.com/gety-ai/gety-sample-connector my-connector
cd my-connector
Review the project structure
manifest.json # Connector metadata and configuration schema
src/index.ts # Connector implementation
src/index.test.ts # Unit tests
src/gen/manifest.d.ts # Generated config type; do not edit
dist/main.js # Built entry loaded by Gety
deno.json # Tasks: build, check, test, verify, runner
dev/runner.ts # Local lifecycle runner
scripts/build.ts # esbuild bundler
dist/main.js is the entry Gety loads. After editing src/index.ts, run deno task build; changing the source file alone doesn't update the built connector.
Plan the document contract first
Before writing code, decide how the source maps to Gety documents. Settle these up front:
- The source and its authentication method.
- A stable document ID that survives across polls.
- How each record maps to
title,content, andmetadata. - How deleted or archived records are detected and removed.
- What link opens when a user selects a search result.
Write a minimal manifest
manifest.json tells Gety how to install and run the connector:
{
"id": "my_connector",
"name": "My Connector",
"version": "0.1.0",
"min_gety_app_version": "0.5.1",
"capabilities": ["data-source"],
"entry": "dist/main.js",
"description": "Indexes documents from My Connector.",
"schedule": { "strategy": "interval", "interval_seconds": 3600 },
"doc_link": { "kind": "url", "field": "url" },
"config": {
"fields": [
{
"id": "api_key",
"title": "API Key",
"type": "password",
"required": true,
"description": "Create this token in the service settings."
}
]
}
}
See Connector reference for every manifest field.
Implement src/index.ts
Your connector is a class that extends Connector<Config, State>. Implement poll() as an async generator that yields batches of document updates and a state checkpoint.
import { Connector, type PollResult, del, upsert } from '@gety-ai/connector-sdk';
import type { ManifestConfig } from './gen/manifest.d.ts';
type State = {
cursor?: string;
};
export default class MyConnector extends Connector<ManifestConfig, State> {
async *poll(): AsyncGenerator<PollResult, void, unknown> {
let cursor = this.lastState?.cursor;
for await (const page of fetchPages(this.config.api_key, cursor, this.signal)) {
yield {
updates: [
...page.items.map((item) =>
upsert({
id: item.id,
title: item.title,
content: item.body,
content_format: 'markdown',
doc_type: 'my:item',
doc_updated_at: item.updatedAt,
metadata: { url: item.url },
}),
),
...page.removed.map((id) => del(id)),
],
state: { cursor: page.next_cursor },
};
cursor = page.next_cursor;
}
}
}
Key points:
upsert(doc)adds or updates a document. Onlyidandtitleare required;idmust stay stable across polls.del(id)removes a document when the source proves it's gone.this.configcontains the values from your manifest configuration fields.this.lastStatecontains the state from the last successful poll progress.this.signalaborts when the user disables or restarts the connector. Pass it to fetches when possible.
Manage state and batches
Yield a state checkpoint with each batch, after the updates it covers. Gety persists that state as lastState for the next poll, so a failed poll can retry from the last committed checkpoint.
Don't advance state before yielding the documents it covers. Yield at a boundary the source can retry safely, such as an API page or a durable cursor.
Avoid these patterns
These mistakes can cause memory pressure or broken links:
- Loading all remote data into memory at once. Prefer paging or streaming the source instead.
- Using temporary URLs in
doc_link. Links must stay stable after the poll ends. - Putting large blobs in
metadata. Keep metadata small and index text throughcontent. - Reading
this.configin the constructor. Configuration is available in lifecycle methods such asonLoad()andpoll(), after Gety injects it.
Verify locally
Run the sample verification task before installing the connector:
deno task verify
verify runs formatting, lint fixes, config type generation, type-checking, tests, and the build. Then exercise the connector lifecycle with the local runner:
cp .env.example .env
deno task runner -- --reset-state
The runner calls onLoad() and poll() and writes each run's output under dev/runs/<timestamp>/: documents, updates, deletes, and state snapshots. Check for stable IDs, expected content, correct metadata, and RFC 3339 timestamps. --reset-state ignores the saved runner state so you can test a clean first poll.
If your .env needs secrets such as an API key, edit the file yourself. Don't paste secrets into a chat with an agent.
Install in Gety
- Run
deno task buildto refreshdist/main.js. - Open Gety → Settings → Connectors and install your connector folder.
- Fill in any required configuration fields in the install dialog.
- Confirm the install.
- Let Gety run the connector according to its update strategy. For a manual connector, click Update now.
After later code edits, run deno task build again and click Restart in Gety to load the new build.
Next steps
For the complete contract, including manifest fields, configuration field types, the SDK lifecycle, and WireDoc fields, see Connector reference.