Custom Connectors
Most of the time, you create a custom connector by describing what you want in plain English and letting Triggerware generate it for you. But once it’s generated, you may want to read or tweak the code yourself — and occasionally you’ll want to write one from scratch. This page explains what’s actually in a connector.
A connector is just two files: a YAML schema that describes the virtual table, and a Python script that produces the rows.
The YAML schema
Section titled “The YAML schema”The YAML file tells Triggerware the shape of your virtual table — what columns it has, what types they are, and what configuration (if any) it needs.
description: > Recent posts from a specific X (Twitter) user. Provide a username to get the date, time, and text of each post.columns: - name: username type: casesensitive description: X handle (e.g. "elonmusk" or "@elonmusk") required: true - name: date type: casesensitive description: Date posted (YYYY-MM-DD) required: false - name: text type: casesensitive description: The content of the post required: falseconfig_schema: api_key: stringEach column needs a name, a type, a description, and the ‘required’ field. Required columns must appear in a query’s WHERE clause for the connector to fire.
Not every generator needs an input column. If the data source is a fixed collection — say, “all ServiceNow incidents” — your generator takes no arguments (other than config) and returns the full set. As a rule of thumb: keyword-style sources (lookup an article, search a username) take one input. Collection-style sources (all incidents, all customers) take none. Most connectors fall into one of these two patterns.
Types are casesensitive (plain string), caseinsensitive (case-insensitive string), int, float, date, or timestamp.
The ‘config_schema’ describes per-connector settings that the user provides; typically API keys, base URLs, account IDs, or anything else the connector needs but shouldn’t be passed in by the caller every time. Each entry maps a name to a plain JSON type. Whatever you list here becomes a value the user sets via the configure endpoint after installing the connector, and the same name becomes a function argument inside your Python generators (see below).
The Python script
Section titled “The Python script”The Python file defines one or more generators — functions that produce rows. Most connectors only have one.
from external_connector import generatorimport httpx
BASE = "https://api.twitter.com/2"
@generator()async def by_username(username: str, api_key: str): username = username.lstrip("@") headers = {"Authorization": f"Bearer {api_key}"} async with httpx.AsyncClient() as client: resp = await client.get(f"{BASE}/users/by/username/{username}", headers=headers) uid = resp.json()["data"]["id"]
resp = await client.get( f"{BASE}/users/{uid}/tweets", headers=headers, params={"tweet.fields": "created_at", "max_results": 100}, ) tweets = resp.json().get("data", [])
rows = [] for tw in tweets: created = tw.get("created_at", "") rows.append([created[:10], created[11:19], tw.get("text", "")]) return rowsFunction arguments match the column names you marked required: true, plus any config_schema keys. The function name itself doesn’t matter.
Generators should always return a list of lists. Each inner list is one row. The columns appear in the same order as in the YAML, minus any columns that were passed in as arguments. In the example above, the function takes username and returns three values per row. Non-async generators wor fine, but only httpx and the standard library (and the external_connector package) are available, so async code is preferred for any API call.
If something goes wrong, raise an exception. Don’t return an empty list or a row containing an error message — those will be silently treated as valid data!
Creating and editing connectors
Section titled “Creating and editing connectors”You don’t write these files into your repo — you upload them to your Triggerware instance. The easiest path is to let the LLM do the first draft:
curl -X POST https://api.triggerware.com/connectors/custom \ -H "Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{...}'Then read and edit either file directly:
curl -X PUT https://api.triggerware.com/connectors/custom/<name>/python \ -H "Api-Key: YOUR_API_KEY" \ -H "Content-Type: text/plain" \ -d "..."curl -X PUT https://api.triggerware.com/connectors/custom/<name>/yaml \ -H "Api-Key: YOUR_API_KEY" \ -H "Content-Type: text/plain" \ -d "..."After editing, install the connector (or re-install, if it was already installed) for your changes to take effect. See the Connectors page for the full lifecycle.