---
title: "Reconnect connection"
method: POST
path: "/connection/{id}/reconnect"
tags: ["Connections"]
---

# Reconnect connection

`POST /connection/{id}/reconnect`

## Request

- Base URL: `https://api.elaichi.ai`
- URL: `https://api.elaichi.ai/connection/{id}/reconnect`
- Auth: HTTP bearer

Mints a fresh `connect_url` for an existing connection; this is 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 and the old stored credential is dropped. The body is optional: an empty POST returns the person to the app root. On an org-owned connector it needs the same `use` level as `POST /connection` (403 `connector_not_shared` once the share was withdrawn). `409 reconnect_not_needed` when the connector is a remote MCP server with no sign-in (auth mode `none`) and the connection is `active`: there is nothing to reconnect, and the row's `can_reconnect` is `false` for the same reason. A `pending` no-sign-in connection (tab closed before the callback) still reconnects; it is the only way to a fresh session.

## Path parameters

- `id` string, required

## Headers

- `X-Organization-Id` string

## Request body

- object
  - `redirect_uri` string: App-relative return path.

## Response `200`

OK

- object
  - `connection` object, required
    - `id` string: Connection id (`conn_…`).
    - `restricted_inherited_from` object, nullable: 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.
      - `slug` string, nullable, required: 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.
      - `label` string, nullable, required: 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".
    - `connector_slug` string: Which connector this account is on.
    - `name` string
    - `owner_user_id` string: Owning member (`usr_…`); calls run as this identity. The owner has implicit `edit`, plus delete and transfer, which 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`.
    - `connected_by_user_id` string, nullable: Who authenticated this account (`usr_…`): the member whose third-party credential Elaichi holds. Written once at creation and never rewritten. It differs from `owner_user_id` on purpose: `POST /connection/{id}/transfer` moves the owner and does not touch the stored credential, so after a transfer these are two different people and only this one should be shown as "Connected by". Null means not recorded, never "nobody": connections created before this field existed are null. Show nothing for a null.
    - `connected_by_label` string, nullable: Display name for `connected_by_user_id` (name, else email). Present on every response that returns a connection, commands included. A null label beside a non-null `connected_by_user_id` means the person has left the organization; show "Former member". Compare the two fields, never one alone: a null id means "not recorded", a null label means "departed".
    - `connector` object: The connector's display data, so a client needs no `GET /connector/{slug}` per distinct connector. On `GET /connection` rows and `GET /connection/{id}` (which adds `can_view_connector`, `available` and `auth_mode`, and always states `single_redirect_auth`); absent from the command responses, which cannot change a connection's connector.
      - `label` string, required: Catalog display name; the slug itself when the catalog cannot resolve it.
      - `logo` string, nullable, required: Logo URL; null when the connector has none.
      - `upstream_slug` string: Present only on a fork (an org-owned connector copied from another): the connector it was forked from. Omitted when the caller has no access to the fork or to a private upstream (naming it would disclose its slug), and when the upstream cannot be read.
      - `single_redirect_auth` boolean: The connector-row hint of the same name: connecting is most likely a single provider sign-in, so a client reserves a popup inside the Reconnect click. Always stated on `GET /connection/{id}` (false when `can_view_connector` is). On a list row it is stated only where the caller could open the connector, and ABSENT otherwise, which reads as unknown, never false.
      - `available` boolean: False only when the catalog no longer has the connector (Elaichi unpublished or deleted a platform connector): the connection is kept but nothing can work until it returns. On `GET /connection/{id}` it is `true` when the connector resolves; on a `GET /connection` row it is never `true`, only `false` or absent. Absent also when the catalog could not answer, so a brief outage never reads as an unpublished connector.
      - `auth_mode` 'oauth_auto' | 'oauth_client' | 'custom' | 'none': `GET /connection/{id}` only, remote MCP connectors only, and only when `can_view_connector`: how the connector signs in, so a screen can say "No sign-in" instead of "API key".
      - `can_view_connector` boolean: `GET /connection/{id}` only. Whether `GET /connector/{slug}` would answer this caller: false for a connector the catalog no longer resolves, without `connector:view`, and for an org-owned connector nobody shared with them (it 404s). Link to the connector only when true.
    - `saffron_account_id` string, nullable: Identifier of the stored credential Elaichi holds for this connection. Opaque; null until the connect flow completes.
    - `status` 'pending' | 'active' | 'needs_reauth' | 'disconnected': `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 when connections are read.
    - `last_error` string, nullable
    - `access` string: The caller's relationship to this connection: `owner`, or a granted `view`/`use`/`edit` level. A connection the caller neither owns nor holds a grant on is not visible to them at all, whatever organization permission they hold.
    - `owner` object: Who `owner_user_id` is. Present on a connection the caller does not own and absent on their own. On every response that returns a connection (list, detail and the command responses), so a transfer names the new owner without a reload. `name` and `email` are absent for an owner who has left the organization ("Former member").
      - `id` string, required: User id (`usr_…`) — same value as `owner_user_id`.
      - `name` string: Display name, else the email.
      - `email` string, email
      - `avatar_url` string, nullable: 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.
    - `shares` AclEntry[]: Present only when the caller may see who the connection is shared with: the owner or `edit` access. A `view` or `use` recipient is never told who else it is shared with. Absent (not empty) when the caller may not see it.
      - `id` string: Grant id (`acl_…`), the `{aclId}` that `DELETE .../share/{aclId}` takes.
      - `resource_type` 'connection' | 'toolbox' | 'template' | 'connector' | 'file'
      - `resource_id` string: The shared resource's id (a connector's slug, for that type).
      - `grantee_type` 'user' | 'team' | 'org': `user` and `team` grants target one `grantee_id`; `org` applies to every member of the organization and takes no id.
      - `grantee_id` string, nullable: User id (`usr_…`) or team id (`team_…`). Null for a `grantee_type: "org"` grant.
      - `level` 'view' | 'use' | 'edit': 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.
      - `created_at` string, date-time
      - `updated_at` string, date-time
    - `created_at` string, date-time
    - `updated_at` string, date-time
    - `can_share` boolean: Whether the caller may share this connection: the owner, `edit` access, or a team admin on a team-granted one, and `connection:share`. Computed by the API, so read it rather than re-deriving it from `owner_user_id` and permissions.
    - `can_manage` boolean: Present on every list and detail row. Whether the caller may edit this connection's own settings: the owner, or `edit` access.
    - `can_transfer` boolean: Present on every list and detail row. Whether the caller may transfer or delete this connection: the owner only. Narrower than `can_manage`, because an `edit` grant lets someone use and reconfigure a connection, never give it away or destroy it.
    - `can_revoke_share` boolean: Present on every list and detail row. Whether the caller may grant or revoke access to this connection. It matches `can_share` and is separate from `can_manage`: an `edit` grantee whose role omits `connection:share` can reconfigure or reconnect the connection but cannot change who else has access. Mirrors `DELETE /connection/{id}/share/{aclId}`.
    - `access_summary` object: Present only when `can_share` is true. A short summary of who has access; page `GET /connection/{id}/share` for the grants themselves.
      - `org_level` 'view' | 'use' | 'null', nullable
      - `team_count` integer
      - `user_count` integer
      - `total` integer: Every grant, the org-wide one included.
      - `preview` object[]: At most 5 grantees, broadest first (org, then teams, then members), for a hover preview.
        - `grantee_type` 'user' | 'team' | 'org'
        - `grantee_id` string, nullable
        - `level` 'view' | 'use'
        - `name` string, nullable: Display name; null for org grants and for grantees no longer in the org.
    - `access_via` 'owner' | 'direct' | 'team' | 'org': How the caller reaches this connection, 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 connection 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`.
    - `access_via_team` object: 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.
      - `id` string: Team id (`team_…`).
      - `name` string, nullable: Team display name, or null when the team no longer resolves in the directory — the same null contract every resolved grantee name carries.
  - `connect_url` string, uri, required: Where the person 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. When they finish, the provider returns them to Elaichi and the status becomes `active`.
  - `direct_connect_url` string, uri, nullable, required: The same one-time session as `connect_url`, but it goes straight to the provider's sign-in instead of the hosted connect page, so the person skips the page. Non-null only when the connector has exactly one auth method, that method is a redirect (OAuth), and it has no form fields or `permissions_text`; those are the cases where the hosted page would show nothing but a Connect button. Open it top-level in a popup or a new tab, never in an iframe. `null` means use `connect_url`; so does a lookup that failed or took longer than 1.5 seconds.

## Other responses

- `400`: Bad request
- `401`: Unauthorized
- `403`: Forbidden
- `404`: Not found
- `429`: Rate limited

---

[API](https://skmtc.dev/elaichi/apis/elaichi-api.md) · [All operations](https://skmtc.dev/elaichi/apis/elaichi-api/llms.txt) · [OpenAPI document](https://skmtc.dev/elaichi/apis/elaichi-api/revisions/b22cfb7b6d15?raw)
