Start an HTTP connector
Building a connector, step 7 (HTTP track, 1 of 5). Previous: Choose a kind.
An HTTP connector is a manifest.json that declares everything about the connector, plus one small
TypeScript module per operation, called a handler. There is no class to write: Owlie's HTTP
engine reads the manifest and calls your handlers. Write the manifest first, from the design you
made in steps 3 to 5. Every handler you write afterwards is typed from it.
The files
A connector is a set of files. The Connector Builder shows them as a file tree. An export carries the same content as one JSON document: the manifest under its own key, and every other file keyed by its path (Pack format).
manifest.json the declaration
handlers/connection/validateConfig.ts
handlers/connection/testConnection.ts
handlers/user/fetchOne.ts one module per operation: handlers/<entity>/<operation>.ts
handlers/user/fetchAll.ts
handlers/team/fetchRelationships.ts
handlers/grants/team_member/add.ts one add.ts and remove.ts per writable Grant kind
handlers/grants/team_member/remove.ts
lib/ shared modules your handlers import, and optional hooks
fixtures/inputs.json saved test inputs for the Test panel
README.md your design notes: vendor facts, assumptions, the Grant kind table
CHANGELOG.md what changed, newest dated entry first
Owlie generates types.gen.ts from the manifest when it builds your connector. Handlers import their
types from it as @connector/types, which reads the same from every folder (the relative path works
too: ../../types.gen.ts from a handler, ../../../types.gen.ts from a Grant handler). It is never
part of an export, and you never edit it. Files under lib/ import the same way, as
@connector/lib/<file>.ts.
Create the connector
In the Connector Builder
- Open Admin > Integrations > Connectors and select the Custom tab.
- Select New connector.
- Under Start from, choose Blank. (A connector export imports a pack instead.)
- Under Kind, choose HTTP.
- Enter a Name. Owlie fills in the Key from it; change the key now if you want another, since it cannot change later.
- Select Create connector.
The builder opens on the draft. Its tabs (Basics, Config variables, Auth, Entities, Grant kinds, Files) edit the manifest through forms, and Files shows every file. The builder saves as you type; there is no save button.
Through the API
Create the connector with createCustomConnector ({ "key": "acme-projects", "name": "Acme Projects", "kind": "http" }), then write its manifest and files into the draft with updateCustomConnectorDraft.
Or build the whole connector locally and import it in one call with customConnectorImport.
Build with the API and agents lists the operations and their inputs.
Write the manifest
The manifest is JSON. This one declares a connector for a fictional project-management system, Acme Projects, with users and teams:
{
"key": "acme-projects",
"version": 1,
"name": "Acme Projects",
"description": "Acme Projects users and teams, synced and provisioned.",
"icon": "@acmeprojects.example",
"kind": "http",
"provides": ["sync", "provision"],
"pagination_type": "cursor",
"config": {
"schema": {
"type": "object",
"required": ["subdomain"],
"properties": {
"subdomain": { "type": "string", "minLength": 1, "title": "Subdomain", "x-suffix": ".acmeprojects.example" }
},
"additionalProperties": false
}
},
"auth": [],
"entities": {},
"grant_kinds": []
}
| Field | What to put there |
|---|---|
key | Lowercase letters, digits, - and _, starting with a letter or digit. It identifies the connector and cannot change. |
version | The major version, 1 for a new connector. |
name, description, icon | What the catalog shows. icon is @ plus the vendor's domain, or an https:// image URL. |
kind | http. |
provides | sync if it reads, plus provision if it creates accounts or writes Grants. |
pagination_type | The default paging style for list calls: cursor, next-token, offset-limit, page-number or link-header. |
config | The settings an install fills in besides credentials, as JSON Schema. |
auth | How it signs in. The next page covers it. |
entities | The entities from step 3, each with its record schema. |
grant_kinds | The Grant kinds from step 4. |
custom_ops | Extra actions workflows and agents can call, if you have any. |
Every field, including the optional ones, is in Connector manifest.
Declare the entities
Each entity has a name, a JSON Schema for its record, its identifiers, and whether it is the
account. Here is the account entity for Acme Projects:
{
"user": {
"name": "User",
"is_account": true,
"identifier_paths": ["id", "email"],
"sync_support": { "supports_incremental": false, "supports_deleted": false },
"schema": {
"type": "object",
"properties": {
"id": { "type": "string", "readOnly": true, "x-identifier": true, "x-provider-stable-id": true },
"email": {
"type": "string",
"format": "email",
"x-identifier": true,
"x-provisioning": { "requiredOn": ["PROVISION"] }
},
"first_name": { "type": "string" },
"last_name": { "type": "string" },
"status": { "type": "string", "enum": ["active", "deactivated"], "readOnly": true }
}
},
"sync_profile": {
"target": "RESOURCE",
"matchingRules": [{ "identityField": "email", "externalExpression": "email" }],
"is_active": "status != 'deactivated'"
}
}
}
sync_profile sets the defaults for the install's account sync:
"target": "RESOURCE"is for a system people are given access to. Sync links each account to an existing Owlie identity throughmatchingRules(here, the identity'semailequals the record'semail) and flags an account that matches nobody.IDENTITYis only for a source of people, such as an HR system: Sync then creates an identity for every account it cannot match. Without atarget, Owlie treats the connector as an identity source, so always declare it.is_activeis an expression over the record's own properties that tells Sync whether the account is active (Expressions). Without it, Sync cannot tell an active account from a deactivated one.
The account also gets a provisioning profile, covered on Write handlers.
The handlers your manifest requires
Your manifest decides which handlers must exist. Publishing refuses a connector that lacks one:
| Handler | Required when |
|---|---|
connection.testConnection | Always |
<entity>.fetchOne | For every entity, including lookup entities |
<entity>.fetchAll | For every entity, when provides includes sync |
<entity>.fetchAllSince | For an entity with sync_support.supports_incremental: true |
<entity>.fetchRelationships | On the discovered_via entity of every per-entity Grant kind |
<account entity>.create | When provides includes provision, unless the account's create_semantics is external |
grant.<kind>.add and grant.<kind>.remove | For every writable Grant kind |
connection.validateConfig is optional: without it, Owlie checks the install against your
configuration schemas. Every other handler is optional, and an operation you leave out is simply not
available. A handler for something the manifest does not declare (an undeclared entity, a read-only
Grant kind, a custom operation missing from custom_ops) is refused.
What Owlie checks
A draft can be incomplete while you work: you can test an add handler before its remove exists. When you import or publish, Owlie checks the manifest and your files together. It refuses, for example, a handler for an entity that does not exist, a writable Grant kind missing its add or remove handler, a Grant kind that names an undeclared entity, and a secret written into the manifest. Each problem has a code and a message; Validation issues explains every code and how to fix it.