List connectors
/connector
Lists the platform catalog plus this organization's own custom connectors. Every member sees the platform catalog. A custom connector is listed only for its owner and the members it is shared with, and is absent (and `404` by slug) for everyone else, whatever their role; `connector:view` and `connector:manage` widen nothing. Connectors the caller's restrictions block are returned, flagged `restricted` and `restricted_by`, not filtered out. Platform catalog rows omit `can_manage` and carry `can_use: true`; on an org-owned row `can_manage` reflects only an `edit` grant of the caller's own. Cursor-paginated: a page holds `limit` rows whenever that many are visible, `prev_cursor` is always null, and the cursor is opaque and valid only for the `search`, `category` and `custom` values it was returned for (anything else is a `400`). A page can occasionally come back empty with a `next_cursor` when many consecutive connectors are hidden from the caller; keep paging until `next_cursor` is null.
Query Parameters
Substring match on the connector name/label.
Exact category match.
Send true to narrow to this organization's own connectors.
Response Body
Set when this connector is restricted for the caller only because the connector it was forked from is. A fork inherits its upstream’s restrictions on purpose (forking a blocked connector is not a bypass), but nothing was set on the fork itself, so this says where the block came from. Null when not restricted, when a rule names this connector directly, or when the upstream could not be traced. Asking for access still targets this connector: approving the fork’s own request opens that fork for that person only (whether the block comes through their role or a rule written about them personally) and the original stays restricted for them; the approved request reads restriction_lifted: true. A sibling of restricted_by, which stays the precedence layer. Do not derive it yourself from the fork’s upstream.
2 properties
The upstream's display name — null, together with slug, when the caller may not be told it. The UI then says "a connector this was forked from".
The upstream connector the restriction is inherited from. For API consumers; never render it. Null — together with label — when the caller may not be told the upstream (an org-owned upstream they hold no grant on, or one that was deleted): a slug names the connector as surely as its label does.
How the caller reaches this connector, not who else can. owner: they own it. direct: a grant names them personally. team: a grant to a team they belong to (or administer), named in access_via_team. org: an organization-wide grant. When several sources apply, the broadest wins and the access level plays no part: owner, else org, else team, else direct. A caller with edit personally and view org-wide reads org, because this field says how far the connector reaches, not what the caller may do with it. It describes only the caller's own access, so a view or use grantee still gets it while the list of other grantees stays hidden. Absent when nothing reaches the caller (for example, the former owner after a transfer of a resource that was never shared); absent never means org.
ownerdirectteamorg
Present exactly when access_via is team, absent otherwise. The team the caller reaches this connector through — one of their own teams, never a disclosure about anybody else.
2 properties
Team id (team_…).
Team display name, or null when the team no longer resolves in the directory — the same null contract every resolved grantee name carries.
True when the connector needs the customer's own OAuth app (bring your own app): a placeholder client id, a real id with a placeholder secret, or a real id with no platform OAuth secret stored for it.
Org-owned rows only. Whether the caller may give this ownerless connector an owner: it has no owner row AND the caller holds connector:manage. Mirrors POST /connector/{slug}/owner naming another member. Independent of the caller's reach, so a manager browsing the catalog can see that a connector needs an owner; it says nothing about who has access.
Org-owned rows only. Whether the caller may make THEMSELVES the owner: can_assign_owner AND a real edit grant of their own. connector:manage alone never makes its holder the owner of a connector nobody shared with them. Mirrors POST /connector/{slug}/owner naming the caller.
Org-owned rows only. Whether the caller may delete this connector: the owner, and only the owner, so false for everyone on an ownerless connector. Mirrors DELETE /connector/{slug}.
Whether the caller may fork the connector. False for a remote MCP connector (POST /connector/{slug}/fork answers 409 connector_not_forkable), for a connector the caller is restricted from, without connector:create, and when the plan does not include custom connectors. On GET /connector rows and GET /connector/{slug}.
Present only on org-owned/custom connector rows (omitted for platform catalog rows, which have no sharing). Whether the caller may edit this connector's config: ownership or an edit grant of their own. The connector:manage permission alone does not imply it, so org owners and admins do not manage a connector nobody shared with them.
Org-owned rows only. Whether the caller may grant or revoke access to this connector: edit access or ownership, and connector:share. This is separate from can_manage: an edit grantee whose role omits connector:share can reconfigure the connector but cannot change who else has access. Mirrors DELETE /connector/{slug}/share/{aclId}.
Org-owned rows only. Whether the caller may transfer this connector — ownership, full stop; no connector:manage fallback. Mirrors POST /connector/{slug}/transfer. False for everyone on an ownerless connector (transfer needs an owner). Not the delete gate — see can_delete.
Whether the caller may create (or re-authenticate) a connection from this connector. That needs the use level that POST /connection and POST /connection/{id}/reconnect enforce (403 connector_not_shared otherwise): ownership or a use-or-better grant (direct, team or org-wide) on an org-owned connector, and always true on a platform catalog row. This is about sharing and is independent of restricted: a connector shared for viewing only is listed but cannot be connected, so leave Connect off rather than show a button that refuses. A connector nobody shared with the caller is not listed.
True for a connector this organization authored or forked.
remote_mcp for an organization's own bring-your-own remote MCP connector (its tools are discovered per connection, not read from the catalog), catalog for every other connector. Read it instead of guessing from the slug. On the detail, a remote MCP connector also carries remote_mcp (url, auth_mode, oauth_registration, catalog_owner, listed_tool_count, the number of tools GET /connector/{slug}/tool lists to this caller with no filter (turned-off tools included for a caller who can manage the connector), and support_contact, the email or https link Elaichi has on file for a connector it publishes, null otherwise) and auth. Its tool_count is the number of tools that are switched on.
catalogremote_mcp
Catalog owner. Org-owned values mark this org's custom connectors. Always this string — the owning MEMBER is owner_member, a separate field, so a client never has to tell a string from an object.
The resolved owning member, the same shape as a toolbox's owner. Org-owned rows the caller reaches (ownership or a grant) only: an object when somebody OTHER than the caller owns the connector (just { id } for an owner who has left the org); null when the connector has no owner yet (an ownerless connector — see POST /connector/{slug}/owner); absent when the caller owns it (owner_user_id already says so) or reaches it by nothing but connector:view.
object · 4 properties
The person's picture: their stored profile picture, else a Gravatar URL (d=404, 96px) derived from their email (the address itself is not sent), else null. Draw initials when it is null or the image fails to load.
User id (usr_…) — same value as owner_user_id.
Owning member (usr_…). Present only on an org-owned row the caller reaches (ownership or a grant) and that has an owner. An ownerless connector has none until POST /connector/{slug}/owner assigns one, and a member browsing with connector:view alone is not told whose it is. The owner has implicit edit, plus delete and transfer, which no share confers.
True when the caller's restrictions block this whole connector. A blocked connector is LISTED and flagged, never omitted — a picker that only got "no match" could not tell "your admin blocked it" from "that provider does not exist". It still cannot be connected: POST /connection and GET /connector/{slug}/tools refuse it.
Which precedence layer the winning rule came from, or null when nothing blocks this connector. Adds which to restricted's whether and nothing else — no rule id, author, reason or coverage. Same field and same meaning as on GET /connector/{slug} and on a connection row.
roleusernull
A hint, not a promise: true when connecting this connector is most likely a provider sign-in with nothing to fill in first — exactly one auth method, and that method can redirect (OAuth). A list row judges the auth methods only; GET /connector/{slug} also rules out a method with form fields or a permissions_text notice. A client may use it to get a popup window ready inside the click that starts a connect. direct_connect_url on the POST /connection / reconnect response stays authoritative.
Stable connector key — this is the identifier everywhere, not an id.
curl -X GET 'https://api.elaichi.ai/connector' \
-H 'Authorization: Bearer $ELAICHI_API_TOKEN' \
-H 'Content-Type: application/json'const response = await fetch('https://api.elaichi.ai/connector', {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + process.env.ELAICHI_API_TOKEN,
'Content-Type': 'application/json',
},
});
const data = await response.json();
console.log(data);import os
import requests
url = "https://api.elaichi.ai/connector"
headers = {
"Authorization": f"Bearer {os.environ['ELAICHI_API_TOKEN']}",
"Content-Type": "application/json",
}
response = requests.get(url, headers=headers)
print(response.json())