connector object: taps, traversal, rate limits, addressing, and
the schema for ingestionConfig. That object is the map ingestion follows
when it talks to the source.
connector.taps, connector.traversal, connector.defaultBaseUrl,
connector.addressingConfig, connector.rateLimits, and
connector.ingestionConfigSchema.
What you set vs what you inspect
Two blobs on the instance are yours. Everything that describes how to walk the API lives on the definition.
GitHub’s
ingestionConfig requires orgId. Linear’s is an optional
baseUrl. Neither lets you change pagination or GraphQL queries.
A sync run
Disabled taps are skipped. Child taps whose parent was skipped or empty do not run.Resolve the URL
Ingestion builds every request from:defaultBaseUrlon the connector (or on the active auth strategy, which wins when set). Linear ishttps://api.linear.app. GitHub ishttps://api.github.com.addressingConfigwhen the host itself is templated.scheme,host, andbasePathcan contain{var}placeholders. Each variable lists asource:user_input(fromingestionConfig),oauth_response(from the token payload, for example a JiracloudId), orstatic.- Path templates on the tap. REST
restConfig.endpointvalues such as/orgs/{orgId}/reposor/repos/{full_name}/issuesare filled fromingestionConfigor from a parent record, depending on the placeholder.
GET https://api.github.com/orgs/{orgId}/repos with {orgId} taken from
the ingestionConfig you sent when you connected.
Walk taps in order
connector.traversal is the graph:
On an edge,
parentKeyField is read from each parent record and substituted
into the child’s endpoint. templateVariable overrides the placeholder name
when it differs from the field. parentContextFields copies extra parent
fields onto each child record (GitHub copies full_name onto pull requests
so later taps still know the repo). parentEligibility can drop a parent
before any child request is planned.
GitHub is a tree: repositories, members, and teams are roots.
repositories.full_name fans out issues, pull_requests, commits,
branches, and releases. pull_requests.number then fans out
pull_request_comments.
Linear is flat: every tap is a root and edges is empty. Each GraphQL
query is independent; issues do not have to be listed before comments.
One tap: request, page, extract
For each enabled tap, ingestion picks the first usable entry intransportOptions (priority 1 is highest). That option is one way to
call the source.
Transport
REST placeholders come from
ingestionConfig or from the parent record on
a traversal edge. GraphQL placeholders usually live in variables and are
updated the same way (and by pagination / incremental injection below).
Incremental window
sync.mode is incremental or full. Incremental taps keep a watermark
on sync.cursorField (for example Linear updatedAt, GitHub
updated_at). The first run uses a lookback window; later runs start from
the last watermark (minus a buffer for late-arriving rows).
maxLookbackDays caps that first window when the source retains only
recent data.
How the window is sent is sync.incrementalParams.filterType:
maxWindowDays splits a long range into sub-windows when the source
rejects large spans. defaultLookaheadDays extends the end past “now” for
forward-looking APIs such as calendars.
If incrementalParams is absent, the watermark is still stored from
cursorField, but the source request may not receive a time filter.
Some taps use sync.asyncQuery: POST to create a job, poll
statusEndpoint until successValue, then page recordsEndpoint. You
will see that block when the source works that way (audit-log exports, for
example).
Pagination
The transport option’spagination.type is how ingestion walks pages until
the stream is exhausted. Details of each type are on
Taps and streams. Cursor pagination also
names cursorPath / hasMorePath (JMESPath into the body) and either
cursorParam (query string) or cursorBodyPath (POST body, including
GraphQL variables.after). cursorIsFullUrl is for APIs that return the
next request as an absolute URL (OData @odata.nextLink).
Records
responseExtraction turns a response body into rows:
Those rows are validated against the tap
schema and land in the
providers pool. Malformed source
records stop at the boundary instead of landing in the lake.
Two worked paths
Linear issues (GraphQL, flat)
The tap is a root. Transport is GraphQL POST /graphql. The query asks for
issues(first: $first, after: $after, filter: { updatedAt: { gt: $since } }).
Static variables set first to 100.
- Ingestion writes the watermark into
variables.since(filterType: body_filter,bodyFilterPath: variables.since). - It POSTs the query.
- It extracts
data.issues.nodes. Each node’sidis the primary key;updatedAtadvances the watermark. - If
data.issues.pageInfo.hasNextPageis true, it putspageInfo.endCursorintovariables.afterand POSTs again.
GitHub pull_requests (REST, child of repositories)
repositories is a root: GET /orgs/{orgId}/repos, Link header
pagination, body is an array (responsePath: "@").
Each repository record has full_name. The edge
repositories -> pull_requests substitutes that into
GET /repos/{full_name}/pulls?per_page=100&state=all&sort=updated.
Incremental runs also send since from the last updated_at watermark.
The JSON body is again an array. GitHub’s next page is the Link header,
not a cursor field. full_name is copied onto each PR record so
pull_request_comments can call
/repos/{full_name}/pulls/{number}/comments without looking the repo up
again.
Rate limits, retries, and skipped parents
connector.rateLimits (and optional per-tap rateLimits) cap requests per
second and max concurrent calls. retry.respectRetryAfter honors the
source’s Retry-After when it is present. GitHub’s definition sits near
1.4 requests/second, so a large backfill is paced on purpose.
On fan-out, skipOnHttpStatus (for example [403, 404]) skips that parent
and continues. errorBodyRules can reclassify a status by inspecting the
body when the source uses one code for several outcomes.
perParentAuth switches the OAuth subject per parent record (Google
Workspace impersonation). You do not configure these; they explain why one
repo 404s and the rest of the sync finishes.
Allowlists in ingestionConfig
Some connectors accept parentDataFilters inside ingestionConfig. When
the tap’s restConfig has parentDataFilterQueryInject or
parentDataFilterBodyInject, those values are written into $filter, a
repeated query param, or the JSON body so the source only returns matching
rows. If the filter key is absent, the tap runs workspace-wide. If the key
is present but empty or the field name does not match, the tap is skipped.
Step-by-step connector setup (OAuth apps, API keys, scopes) lives in the
product documentation.