Reconnect connection
/connection/{id}/reconnect
Mints a fresh `connect_url` for an existing connection — the fix for `needs_reauth`. The connection id and every toolbox entry pinned to it survive, so nothing has to be rebuilt; the new account replaces the old binding through the same attribution-checked callback, and the stale vault entry is dropped. The body is optional: an empty POST returns the user to the app root.
Path Parameters
Connection id (conn_…).
Request Body
App-relative return path.
Response Body
Where the USER authenticates with the provider. Hand it to them as a link — Elaichi never accepts a password or API key for a third-party system through this API. Single-use and short-lived; mint another with reconnect. On completion the provider returns to GET /connection/{id}/callback and the status becomes active.
The caller's relationship to this connection: owner, or a granted view/use/edit level. There is no oversight fallback for connections — this is never "oversight"; a connection the caller neither owns nor holds a grant on is not visible to them at all, whatever organization permission they hold.
Present only when can_share is true. Bounded by design — page GET /connection/{id}/share for the grants themselves.
5 properties
viewusenull
At most 5 grantees, broadest first (org, then teams, then members), for a hover preview.
4 properties
userteamorg
viewuse
Display name; null for org grants and for grantees no longer in the org.
Every grant, the org-wide one included.
How the CALLER reaches this connection — not who else can. owner — they own it. direct — a grant naming them personally. team — a grant to a team they belong to (or, per §6.2, one they administer), named in access_via_team. org — an organization-wide grant. When several sources apply the BROADEST wins and the caller's access level is not consulted: owner, else org, else team, else direct. So a caller granted edit personally AND view org-wide reads org — a narrower grant must never mask org-wide exposure, since this field says how far the connection reaches, not what the caller may do with it. Deliberately NOT gated on can_see_shares, and deliberately not a widening of it: this is the caller's OWN grant and their OWN team memberships, so a view/use grantee receives it while the grantee list — information about colleagues — stays closed to them. No other member is ever named. ABSENT when nothing reaches the caller — a transfer answering the ex-owner of a resource that was never shared, or a catalog row browsed with no grant behind it. Absent means "no source to name", never "not permitted", and never an implied org.
ownerdirectteamorg
Present exactly when access_via is team, absent otherwise. The team the caller reaches this connection 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.
Present on every list and detail row. Whether the caller may edit this connection's own settings — owner, or edit access. No connection:manage fallback. Computed server-side; same can_manage name used by toolbox and connector for the identical capability.
can_share, verbatim — a THIRD formula, deliberately not an alias for can_manage: an edit grantee whose role omits connection:share (Auditor, Guest, any custom role) can reconfigure or reconnect the connection but was never meant to grant or revoke someone else's access to it, so can_manage: true on that row must not imply this control too. Mirrors DELETE /connection/{id}/share/{aclId}'s own gate exactly. Present on every list and detail row.
Whether the caller may share this connection — owner, edit access, or a team admin on a team-granted one. No connection:manage fallback. Computed server-side (CLAUDE.md's API-first rule); the console reads this rather than re-deriving it from owner_user_id/permissions.
Present on every list and detail row. Whether the caller may transfer or delete this connection — owner, full stop. No connection:manage fallback. Deliberately narrower than can_manage: an edit grant lets someone use and reconfigure a connection, never give it away or destroy it.
Display name for connected_by_user_id, resolved server-side one batch per page (name, else email). Present on every branch that returns a connection, commands included. A null HERE beside a NON-null connected_by_user_id is a fact about the person, not a failed lookup: they have left the organization, and clients render "Former member" — the same word the GET /audit-log actor summary uses. Compare the two fields, never one alone: a null id means "not recorded", a null label means "departed".
Who AUTHENTICATED this account (usr_…) — the member whose third-party credential the vault actually holds. Written once at creation and never rewritten. Distinct from owner_user_id on purpose: POST /connection/{id}/transfer moves the owner and deliberately does NOT touch the vault account, so after a transfer these are two different people and only this one may be rendered as "Connected by". Null means NOT RECORDED, never "nobody" — every connection created before this field existed is null, because nothing server-side could tell a never-transferred row (where owner_user_id would have been the honest answer) from a transferred one, and a fabricated attribution is worse than an absent one. Render nothing for a null.
Which connector this account is on.
Connection id (conn_…).
Owner summary. Currently never populated on a connection response — no list route resolves it today — but kept in the schema in case a future route does; read owner_user_id plus a member lookup instead.
3 properties
User id (usr_…) — same value as owner_user_id.
Owning member (usr_…); calls run as this identity. Owner is implicit edit, plus delete and transfer — the two things no share confers. Move it with POST /connection/{id}/transfer. This is who is ACCOUNTABLE for the connection now, which after a transfer is not who authenticated it — see connected_by_user_id.
Vault account holding the credential. Null until the connect flow completes.
Present only when the caller may see the ACL: owner or edit access. A view/use recipient is never told who else the connection is shared with. Absent (not empty) when the caller may not see it.
8 properties
User id (usr_…) or team id (team_…). Null for a grantee_type: "org" grant.
user and team grants target one grantee_id; org applies to every member of the organization and takes no id.
userteamorg
ACL entry id — the :aclId a DELETE .../share/{aclId} call takes.
Same ladder for every shareable resource, low to high. view — see it exists, read its metadata/config; cannot exercise it. use — view + exercise it, resource-specific: run tools through a connection; execute a toolbox's tools through its bound connections (this DELEGATES — the caller runs through each entry's pinning editor's own authority, not necessarily their own); stamp a new toolbox by copying a template's entries; create connections from a connector. edit — use + change its settings, entries and its own grants.
viewuseedit
The shared resource's id (a connector's slug, for that type).
connectiontoolboxtemplateconnector
pending = created but not yet authenticated (open connect_url); active = usable; needs_reauth = the credential expired or was revoked upstream, call reconnect; disconnected = no longer usable. Refreshed lazily against the vault when connections are read.
pendingactiveneeds_reauthdisconnected
curl -X POST 'https://api.elaichi.ai/connection/<id>/reconnect' \
-H 'Authorization: Bearer $ELAICHI_API_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"redirect_uri":"your_redirect_uri"}'const body = {
"redirect_uri": "your_redirect_uri"
};
const response = await fetch('https://api.elaichi.ai/connection/<id>/reconnect', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + process.env.ELAICHI_API_TOKEN,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
const data = await response.json();
console.log(data);import os
import requests
url = "https://api.elaichi.ai/connection/<id>/reconnect"
headers = {
"Authorization": f"Bearer {os.environ['ELAICHI_API_TOKEN']}",
"Content-Type": "application/json",
}
payload = {
"redirect_uri": "your_redirect_uri"
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())