{
  "openapi": "3.1.0",
  "info": {
    "title": "Unlinked canonical beta",
    "version": "1.0.0",
    "description": "Current authenticated REST API at /api/agent/v1/ and scoped MCP. Manual API key setup: https://www.unlinked.ai/agents. Lifecycle and release contract: docs/agent-key-settings.md in the repository. Manual-key controls and connector choice: docs/agent-key-settings.md. Historical-grant normalization, tool refresh and OAuth permission consent: docs/agent-api.md (repository)."
  },
  "servers": [
    {
      "url": "https://www.unlinked.ai"
    }
  ],
  "paths": {
    "/login": {
      "get": {
        "summary": "Start Ideaflow ID sign-in",
        "responses": {
          "303": {
            "description": "Redirect to verified provider"
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "summary": "Streamable HTTP MCP, account-scoped bearer",
        "security": [
          {
            "accountGrant": []
          },
          {
            "connectorOAuth": [
              "network",
              "people"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "MCP protocol response"
          },
          "401": {
            "description": "Missing, invalid or revoked account grant",
            "headers": {
              "WWW-Authenticate": {
                "description": "Bearer challenge with resource_metadata pointing at /.well-known/oauth-protected-resource/mcp (RFC 9728)",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/people": {
      "get": {
        "summary": "Browse/search the published public professional People index without sign-in",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "best",
                "exact"
              ],
              "default": "best"
            },
            "description": "best: every word in any form and order, falling back to people who match some words (see match). exact: the typed phrase as written."
          },
          {
            "name": "presence",
            "in": "query",
            "required": false,
            "description": "member: confirmed current account; legacy: verified recovered account awaiting claim; shadow: imported contact. Omit for everyone. Bound into the cursor.",
            "schema": {
              "type": "string",
              "enum": [
                "member",
                "legacy",
                "shadow"
              ]
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Safe profile summaries and optional nextCursor With a query, the body also carries match (all, some or none) and total."
          },
          "400": {
            "description": "Invalid query/cursor"
          },
          "429": {
            "description": "Public read rate or concurrency bound"
          },
          "503": {
            "description": "Published complete index unavailable"
          }
        }
      },
      "head": {
        "summary": "Probe public People status and JSON headers without a response body",
        "security": [],
        "responses": {
          "200": {
            "description": "Public route headers without a response body"
          },
          "400": {
            "description": "Invalid query/cursor"
          },
          "429": {
            "description": "Public read rate or concurrency bound"
          },
          "503": {
            "description": "Published complete index unavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/paths/~1api~1people/get/parameters/0"
          },
          {
            "$ref": "#/paths/~1api~1people/get/parameters/1"
          },
          {
            "$ref": "#/paths/~1api~1people/get/parameters/2"
          },
          {
            "$ref": "#/paths/~1api~1people/get/parameters/3"
          }
        ]
      }
    },
    "/api/people/{id}": {
      "get": {
        "summary": "Read a public professional profile and public connection summaries",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Safe public professional profile"
          },
          "404": {
            "description": "Profile absent from complete publication"
          },
          "503": {
            "description": "Index unavailable"
          }
        },
        "description": " Public summaries and profiles include detailLevel (basic or detailed), derived only from visible content independently of membership. Profile connectionCount is the full published count and connectionsTotal is the current filtered count. Browser-only connections browsing is /people/{id}/connections?q=...&sort=detail|name; API cursor ordering remains alphabetical. Professional detail fields are defined by detailSchema in src/components/public-directory/contract.ts; link sourcing and privacy are documented in docs/profile-details.md."
      },
      "head": {
        "summary": "Probe public People status and JSON headers without a response body",
        "security": [],
        "responses": {
          "200": {
            "description": "Public route headers without a response body"
          },
          "404": {
            "description": "Profile absent from complete publication"
          },
          "503": {
            "description": "Index unavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/paths/~1api~1people~1{id}/get/parameters/0"
          }
        ]
      }
    },
    "/people/{id}/photo": {
      "get": {
        "summary": "Read an operator-published profile photo (also HEAD); people summaries carry its versioned URL as `photo` when one exists",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
            }
          },
          {
            "name": "v",
            "in": "query",
            "required": false,
            "description": "Content version from the `photo` URL; the exact current version is cacheable for a year.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "image/jpeg, image/png or image/webp with ETag; same-origin resource policy"
          },
          "304": {
            "description": "Not modified (If-None-Match)"
          },
          "404": {
            "description": "No published photo for this id"
          },
          "429": {
            "description": "Photo read rate or concurrency bound"
          }
        }
      }
    },
    "/api/my-connections": {
      "get": {
        "summary": "Read recorded directed 1/2-hop public paths from the signed member's explicitly confirmed legacy profile",
        "security": [
          {
            "browserSession": []
          }
        ],
        "parameters": [
          {
            "name": "degree",
            "in": "query",
            "schema": {
              "type": "integer",
              "enum": [
                1,
                2
              ],
              "default": 1
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 256
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 2048
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Owner-anchored bounded profiles, paths, total, nextCursor and source revision"
          },
          "400": {
            "description": "Invalid degree, query or cursor"
          },
          "401": {
            "description": "Sign-in required"
          },
          "409": {
            "description": "No verified legacy graph anchor"
          },
          "503": {
            "description": "Graph unavailable or changed"
          }
        }
      }
    },
    "/api/legacy-files": {
      "get": {
        "summary": "List own recovered original files after explicit legacy confirmation; no agent access",
        "security": [
          {
            "browserSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Owner-scoped recovered file list"
          },
          "401": {
            "description": "Browser session required"
          },
          "404": {
            "description": "Recovered object unavailable to this owner"
          },
          "503": {
            "description": "Recovery unavailable"
          }
        }
      }
    },
    "/legacy-files/{objectId}": {
      "get": {
        "summary": "Download own preserved original; browser-only, not public or agent-accessible",
        "security": [
          {
            "browserSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Original private CSV bytes"
          },
          "401": {
            "description": "Browser session required"
          },
          "404": {
            "description": "Recovered object unavailable to this owner"
          },
          "503": {
            "description": "Recovery unavailable"
          }
        },
        "parameters": [
          {
            "name": "objectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      }
    },
    "/api/agent/v1/whoami": {
      "get": {
        "summary": "Grant owner identity: stable owner id, grant scope/version/tools, import count, legacy anchor",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "responses": {
          "200": {
            "description": "Owner summary; no contact email/phone"
          },
          "401": {
            "description": "not_linked or grant_revoked (typed error body)"
          }
        }
      }
    },
    "/api/agent/v1/people": {
      "get": {
        "summary": "Deterministic published public People listing (opaque cursor, max 50/page, total and revision)",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "best",
                "exact"
              ]
            }
          },
          {
            "name": "presence",
            "in": "query",
            "required": false,
            "description": "member: confirmed current account; legacy: verified recovered account awaiting claim; shadow: imported contact. Omit for everyone. Bound into the cursor.",
            "schema": {
              "type": "string",
              "enum": [
                "member",
                "legacy",
                "shadow"
              ]
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "best (default): relevance with q, else stored name order; name: A–Z from the first letter or number, ignoring leading emoji/punctuation, accents and case (names are returned as written); name-desc; raw: Unicode code-point order of the name as written. Bound into the cursor.",
            "schema": {
              "type": "string",
              "enum": [
                "best",
                "name",
                "name-desc",
                "raw"
              ]
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of public profile summaries. Each person may carry presence (member: confirmed current account; legacy: verified recovered account awaiting claim; shadow: imported contact) and connectionCount (connections in both directions)."
          },
          "400": {
            "description": "invalid_input or cursor_invalid"
          },
          "401": {
            "description": "not_linked or grant_revoked"
          },
          "403": {
            "description": "scope_not_granted"
          },
          "429": {
            "description": "rate_limited"
          }
        }
      }
    },
    "/api/agent/v1/people/{id}": {
      "get": {
        "summary": "One published public profile with its public connections page",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 160
            }
          },
          {
            "name": "connectionsCursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Public profile detail, with presence and connectionCount when known, and its connections as summaries."
          },
          "401": {
            "description": "not_linked or grant_revoked"
          },
          "404": {
            "description": "not_found"
          }
        }
      }
    },
    "/api/agent/v1/connections": {
      "get": {
        "summary": "Deterministic owner connections: degree 1 includes owner-imported contacts, one entry per person with sources[] (or one row per source with grouping=none); degree 2 only recorded public paths with proving provenance",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "parameters": [
          {
            "name": "degree",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "enum": [
                1,
                2
              ]
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 256
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "name (default): A–Z from the first letter or number, ignoring leading emoji/punctuation, accents and case (names are returned exactly as written); raw: Unicode code-point order of the name as written; name-desc; connected: most recently connected first; imported: most recently imported first; company: A–Z by company, none last. Bound into the cursor.",
            "schema": {
              "type": "string",
              "enum": [
                "name",
                "raw",
                "name-desc",
                "connected",
                "imported",
                "company"
              ]
            }
          },
          {
            "name": "grouping",
            "in": "query",
            "required": false,
            "description": "Degree 1. person (default): one entry per person, grouped only on exact identity evidence — the same published profile (merges followed) or the same LinkedIn address, never a name — with sources[] listing every underlying record and top-level fields from the owner import; total counts people. none: one row per source record (pre-grouping shape). Bound into the cursor.",
            "schema": {
              "type": "string",
              "enum": [
                "person",
                "none"
              ]
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of connection entries with typed provenance and visibility labels. Grouped entries (degree 1, grouping person) also carry sources[] ({ id, name, provenance, visibility }) and, when known, publishedProfileId, linkedinRefHash, connectedAt and importedAt."
          },
          "400": {
            "description": "invalid_input or cursor_invalid"
          },
          "401": {
            "description": "not_linked or grant_revoked"
          },
          "409": {
            "description": "degree_unproven"
          },
          "429": {
            "description": "rate_limited"
          }
        }
      }
    },
    "/api/agent/v1/connection-requests": {
      "get": {
        "summary": "Read-only: the owner's open connection requests (direction=received|sent)",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "parameters": [
          {
            "name": "direction",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "received",
                "sent"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Requests with names, public profile ids, optional notes and ISO times; owner_private"
          },
          "401": {
            "description": "not_linked or grant_revoked (typed error body)"
          },
          "403": {
            "description": "scope_not_granted when the effective grant does not permit this tool (see docs/agent-api.md)"
          }
        }
      }
    },
    "/api/agent/v1/notifications": {
      "get": {
        "summary": "Read-only: the owner's newest in-app notifications with unseen/unread counts",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Notifications (kind, actor name/profile id, ISO time, read); reading marks nothing"
          },
          "401": {
            "description": "not_linked or grant_revoked (typed error body)"
          },
          "403": {
            "description": "scope_not_granted when the effective grant does not permit this tool (see docs/agent-api.md)"
          }
        }
      }
    },
    "/api/agent/v1/ai-search": {
      "post": {
        "summary": "Explicit AI people search: scope mine (own network) or everyone (public index, public-scope grant)",
        "description": "Owner-network AI search requires supplied title/position evidence for a requested role, omits domain-only matches, and gives short conversational reasons grounded in supplied fields, stating when sector focus is unknown; response shapes and grant versions are unchanged.",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "query": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 1024
                  },
                  "scope": {
                    "type": "string",
                    "enum": [
                      "everyone",
                      "mine"
                    ]
                  },
                  "timeoutMs": {
                    "type": "integer",
                    "minimum": 1000,
                    "maximum": 40000
                  }
                },
                "required": [
                  "query"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ranked matches with reasons"
          },
          "401": {
            "description": "not_linked or grant_revoked"
          },
          "403": {
            "description": "scope_not_granted"
          },
          "429": {
            "description": "rate_limited"
          },
          "503": {
            "description": "upstream_unavailable (including elapsed timeoutMs)"
          }
        }
      }
    },
    "/api/agent/v1/search-network": {
      "post": {
        "summary": "Launch tool over HTTP: AI owner-network search or recorded public paths by degree",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "query": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 1024
                  },
                  "degree": {
                    "type": "integer",
                    "enum": [
                      1,
                      2
                    ]
                  },
                  "cursor": {
                    "type": "string"
                  }
                },
                "required": [
                  "query"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Search or recorded-path result"
          },
          "401": {
            "description": "not_linked or grant_revoked"
          },
          "409": {
            "description": "degree_unproven"
          }
        }
      }
    },
    "/api/agent/v1/search-everyone": {
      "post": {
        "summary": "Launch tool over HTTP: AI search over the published public People index",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "query": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 1024
                  }
                },
                "required": [
                  "query"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ranked public matches"
          },
          "401": {
            "description": "not_linked or grant_revoked"
          },
          "403": {
            "description": "scope_not_granted"
          }
        }
      }
    },
    "/api/agent/v1/provision-grant": {
      "post": {
        "summary": "Server-to-server: allow-listed confidential client ensures a read-only account grant for a verified issuer+subject binding (never email). Disabled (404) unless the operator configured the allow list.",
        "security": [
          {
            "provisionClientBasic": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "issuer": {
                    "type": "string",
                    "maxLength": 256
                  },
                  "subject": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  }
                },
                "required": [
                  "issuer",
                  "subject"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ kind, ownerId, grantId, created, version, scope, tools, accessToken } — token only in this response body, never logged"
          },
          "400": {
            "description": "invalid_input"
          },
          "401": {
            "description": "not_linked (person has no owner binding) or grant_revoked (owner turned agent access off)"
          },
          "403": {
            "description": "client_unauthorized"
          },
          "404": {
            "description": "not_found (provisioning disabled on this runtime)"
          },
          "429": {
            "description": "rate_limited (per-client budget; Retry-After)"
          },
          "503": {
            "description": "upstream_unavailable (including an unavailable audit sink — provisioning fails closed rather than going unaudited)"
          }
        }
      }
    },
    "/.well-known/oauth-protected-resource/mcp": {
      "get": {
        "summary": "OAuth protected resource metadata for /mcp (RFC 9728); also served at /.well-known/oauth-protected-resource",
        "responses": {
          "200": {
            "description": "resource, authorization_servers, scopes_supported, bearer_methods_supported",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/oauth-authorization-server": {
      "get": {
        "summary": "OAuth authorization server metadata (RFC 8414)",
        "responses": {
          "200": {
            "description": "Authorization server metadata, including client_id_metadata_document_supported and authorization_response_iss_parameter_supported",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/oauth/authorize": {
      "get": {
        "summary": "Authorization endpoint: signs the person in with Ideaflow ID if needed and shows consent. Requires response_type=code, PKCE S256 and a registered connector-callback or http://localhost / 127.0.0.1 loopback redirect_uri.",
        "responses": {
          "200": {
            "description": "Consent page"
          },
          "303": {
            "description": "Redirect to sign-in, or to the client with code, state and iss (or an error)"
          },
          "400": {
            "description": "Unknown client or unregistered redirect URI (never redirected)"
          }
        }
      }
    },
    "/oauth/token": {
      "post": {
        "summary": "Token endpoint (authorization_code only; public clients)",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "grant_type",
                  "code",
                  "code_verifier",
                  "client_id"
                ],
                "properties": {
                  "grant_type": {
                    "type": "string",
                    "enum": [
                      "authorization_code"
                    ]
                  },
                  "code": {
                    "type": "string"
                  },
                  "code_verifier": {
                    "type": "string"
                  },
                  "client_id": {
                    "type": "string"
                  },
                  "redirect_uri": {
                    "type": "string",
                    "description": "Required when the authorization request included it"
                  },
                  "resource": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "access_token, token_type Bearer, scope",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "OAuth error (invalid_grant, invalid_target, unsupported_grant_type, ...)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/oauth/register": {
      "post": {
        "summary": "Dynamic client registration (RFC 7591); public clients only, redirect URIs limited to Claude/ChatGPT connector callbacks and loopback",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "redirect_uris"
                ],
                "properties": {
                  "redirect_uris": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "client_name": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registered client with token_endpoint_auth_method none",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "invalid_redirect_uri or invalid_client_metadata",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/oauth/revoke": {
      "post": {
        "summary": "Token revocation (RFC 7009) for the client the token was issued to",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "token",
                  "client_id"
                ],
                "properties": {
                  "token": {
                    "type": "string"
                  },
                  "client_id": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Revoked, or nothing to revoke"
          }
        }
      }
    },
    "/api/agent/v1/connection-requests/send": {
      "post": {
        "summary": "Send a member connection request (explicit opt-in only)",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "profileId": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 160
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 300
                  }
                },
                "required": [
                  "profileId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Request updated"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Invalid/revoked grant"
          },
          "403": {
            "description": "Opt-in scope not granted"
          },
          "404": {
            "description": "Request/profile not found"
          },
          "409": {
            "description": "Request state conflict"
          },
          "429": {
            "description": "Shared request/write budget exhausted"
          },
          "503": {
            "description": "Upstream unavailable"
          }
        }
      }
    },
    "/api/agent/v1/connection-requests/accept": {
      "post": {
        "summary": "Accept a member connection request (explicit opt-in only)",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64
                  }
                },
                "required": [
                  "id"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Request updated"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Invalid/revoked grant"
          },
          "403": {
            "description": "Opt-in scope not granted"
          },
          "404": {
            "description": "Request/profile not found"
          },
          "409": {
            "description": "Request state conflict"
          },
          "429": {
            "description": "Shared request/write budget exhausted"
          },
          "503": {
            "description": "Upstream unavailable"
          }
        }
      }
    },
    "/api/agent/v1/connection-requests/ignore": {
      "post": {
        "summary": "Ignore a member connection request (explicit opt-in only)",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64
                  }
                },
                "required": [
                  "id"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Request updated"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Invalid/revoked grant"
          },
          "403": {
            "description": "Opt-in scope not granted"
          },
          "404": {
            "description": "Request/profile not found"
          },
          "409": {
            "description": "Request state conflict"
          },
          "429": {
            "description": "Shared request/write budget exhausted"
          },
          "503": {
            "description": "Upstream unavailable"
          }
        }
      }
    },
    "/api/agent/v1/connection-requests/withdraw": {
      "post": {
        "summary": "Withdraw a member connection request (explicit opt-in only)",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64
                  }
                },
                "required": [
                  "id"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Request updated"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Invalid/revoked grant"
          },
          "403": {
            "description": "Opt-in scope not granted"
          },
          "404": {
            "description": "Request/profile not found"
          },
          "409": {
            "description": "Request state conflict"
          },
          "429": {
            "description": "Shared request/write budget exhausted"
          },
          "503": {
            "description": "Upstream unavailable"
          }
        }
      }
    },
    "/search-public": {
      "get": {
        "summary": "Search published professional People as compact HTML without a connector",
        "security": [],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "mode",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "best",
                "exact"
              ],
              "default": "best"
            }
          },
          {
            "name": "presence",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "member",
                "legacy",
                "shadow"
              ]
            },
            "description": "member: confirmed current account; legacy: verified recovered account awaiting claim; shadow: imported contact. Omit for everyone. Bound into the cursor."
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 2048
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Public-only HTML, up to 50 summaries and next-page link"
          },
          "400": {
            "description": "Invalid public search input"
          },
          "429": {
            "description": "Public request capacity exceeded; Retry-After supplied"
          },
          "503": {
            "description": "Published People unavailable"
          }
        },
        "description": "Experimental alternate transport restrictions and reader verification requirements are owned by docs/public-directory.md#alternate-public-transport in the repository."
      },
      "head": {
        "summary": "Probe public search status and HTML headers without a response body",
        "security": [],
        "parameters": [
          {
            "$ref": "#/paths/~1search-public/get/parameters/0"
          },
          {
            "$ref": "#/paths/~1search-public/get/parameters/1"
          },
          {
            "$ref": "#/paths/~1search-public/get/parameters/2"
          },
          {
            "$ref": "#/paths/~1search-public/get/parameters/3"
          }
        ],
        "responses": {
          "200": {
            "description": "Public route headers without a response body"
          },
          "400": {
            "description": "Invalid public search input"
          },
          "429": {
            "description": "Public request capacity exceeded; Retry-After supplied"
          },
          "503": {
            "description": "Published People unavailable"
          }
        },
        "description": "Experimental alternate transport restrictions and reader verification requirements are owned by docs/public-directory.md#alternate-public-transport in the repository."
      },
      "servers": [
        {
          "url": "https://www.unlinked.ai",
          "description": "Canonical standalone public search"
        },
        {
          "url": "https://unlinked-ideaflowco.vercel.app",
          "description": "Experimental anonymous relay for this path only; ordinary ChatGPT access is not established"
        }
      ]
    },
    "/api/messaging/v1/recipient": {
      "post": {
        "summary": "Confidential OpenChat recipient resolver",
        "description": "Dedicated service credential only; not an account grant or browser API. Resolves published ownership using the active Ideaflow binding. No message is sent.",
        "security": [
          {
            "messagingService": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "profileId"
                ],
                "additionalProperties": false,
                "properties": {
                  "profileId": {
                    "type": "string",
                    "maxLength": 160
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Member identity (server only), unclaimed, or unavailable."
          },
          "401": {
            "description": "Service credential required."
          },
          "503": {
            "description": "Messaging unavailable; retry."
          }
        }
      }
    },
    "/messages/api/conversations": {
      "get": {
        "summary": "Native web inbox (browser session only)",
        "description": "Same-origin browser session only; writes under /messages/api also require the X-Unlinked-CSRF header. Lists the signed-in member's OpenChat conversations through Unlinked's server, which holds the member's short-lived OpenChat credential and never returns it. Responses are projected: no email fields. Not an account-grant or MCP endpoint; agent grants cannot read or send messages.",
        "responses": {
          "200": {
            "description": "No-store projected conversation list"
          },
          "401": {
            "description": "Sign in required"
          },
          "403": {
            "description": "Cross-site request rejected"
          },
          "503": {
            "description": "Messages unavailable"
          }
        },
        "security": [
          {
            "browserSession": []
          }
        ]
      }
    },
    "/c/{token}/add": {
      "post": {
        "summary": "Browser-only: join and add the owner of a shared contact card",
        "description": "Requires a same-origin form POST and session CSRF when signed in. Guests complete verified Ideaflow login before connecting. Card visibility and owner activity are rechecked. No bearer/agent access. Viewing alone never connects.",
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9A-Za-z]{24}$"
            }
          }
        ],
        "responses": {
          "303": {
            "description": "Provider signup/sign-in or contact card result"
          },
          "400": {
            "description": "Invalid intent or CSRF"
          },
          "403": {
            "description": "Wrong origin"
          },
          "404": {
            "description": "Card unavailable"
          }
        }
      }
    },
    "/api/agent/v1/contacts/lookup": {
      "post": {
        "summary": "Read-only, owner-scoped: resolve one of the owner's own contacts without paging",
        "operationId": "unlinked_lookup_contact",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "connectionId": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 128
                  },
                  "linkedinUrl": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  },
                  "profileId": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 160
                  },
                  "refHashes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "pattern": "^[a-f0-9]{64}$"
                    },
                    "minItems": 1,
                    "maxItems": 100
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Owner contact or public profile"
          },
          "400": {
            "description": "invalid_input (typed error body; overlayCode for overlay refusals)"
          },
          "401": {
            "description": "not_linked or grant_revoked"
          },
          "403": {
            "description": "scope_not_granted"
          },
          "404": {
            "description": "not_found"
          },
          "429": {
            "description": "rate_limited"
          },
          "503": {
            "description": "upstream_unavailable"
          }
        }
      }
    },
    "/api/agent/v1/private/person": {
      "get": {
        "summary": "unlinked_get_person_private: Read the owner’s private card about an Unlinked person: profileId may be a published profile id, a connection id from unlinked_list_connections (an imported LinkedIn contact) or a linkedin.com/in/ address.",
        "description": "Read the owner’s private card about an Unlinked person: profileId may be a published profile id, a connection id from unlinked_list_connections (an imported LinkedIn contact) or a linkedin.com/in/ address. Returns the notes and relations recorded about that person (with note and link ids for corrections), plus profileId, connectionId and linkedinRefHash when they apply. entities is empty when nothing is recorded yet. Notes and relations carry author (“owner” or “agent:<client>”), source (“app”, “connector”, “direct-key” or “suggestion”) and assertion (“stated” or “inferred”); null on records made before provenance was kept. Relations also carry relationType. Owner-only private knowledge: visible only to the owner (never to the person it is about), stored in the owner’s Ideaflow people overlay and shown in Unlinked (person and contact pages) and OpenChat. Never notifies anyone. It is NOT a connection request and NOT messaging.",
        "operationId": "unlinked_get_person_private",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "parameters": [
          {
            "name": "profileId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 512
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Owner-only private result (visibility owner_private)"
          },
          "400": {
            "description": "invalid_input (typed error body; overlayCode for overlay refusals)"
          },
          "401": {
            "description": "not_linked or grant_revoked"
          },
          "403": {
            "description": "scope_not_granted: Private people notes & relations is off for this key, or not consented for this OAuth app"
          },
          "404": {
            "description": "not_found"
          },
          "409": {
            "description": "ambiguous_name (with candidates) or identity_unavailable"
          },
          "429": {
            "description": "rate_limited"
          },
          "503": {
            "description": "upstream_unavailable"
          }
        }
      }
    },
    "/api/agent/v1/private/thing": {
      "get": {
        "summary": "unlinked_get_private_thing: Read one of the owner’s private entities (a saved person, company, idea or project, or any entity id from another private tool) with its notes and relations.",
        "description": "Read one of the owner’s private entities (a saved person, company, idea or project, or any entity id from another private tool) with its notes and relations. Notes and relations carry author (“owner” or “agent:<client>”), source (“app”, “connector”, “direct-key” or “suggestion”) and assertion (“stated” or “inferred”); null on records made before provenance was kept. Relations also carry relationType. Owner-only private knowledge: visible only to the owner (never to the person it is about), stored in the owner’s Ideaflow people overlay and shown in Unlinked (person and contact pages) and OpenChat. Never notifies anyone. It is NOT a connection request and NOT messaging.",
        "operationId": "unlinked_get_private_thing",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "parameters": [
          {
            "name": "thingId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Owner-only private result (visibility owner_private)"
          },
          "400": {
            "description": "invalid_input (typed error body; overlayCode for overlay refusals)"
          },
          "401": {
            "description": "not_linked or grant_revoked"
          },
          "403": {
            "description": "scope_not_granted: Private people notes & relations is off for this key, or not consented for this OAuth app"
          },
          "404": {
            "description": "not_found"
          },
          "409": {
            "description": "ambiguous_name (with candidates) or identity_unavailable"
          },
          "429": {
            "description": "rate_limited"
          },
          "503": {
            "description": "upstream_unavailable"
          }
        }
      }
    },
    "/api/agent/v1/private/things": {
      "get": {
        "summary": "unlinked_list_private_things: List the owner’s private entities (saved people, companies, ideas and projects, and Unlinked or OpenChat people they wrote about), optionally by name and kind.",
        "description": "List the owner’s private entities (saved people, companies, ideas and projects, and Unlinked or OpenChat people they wrote about), optionally by name and kind. Use an id with unlinked_get_private_thing. Owner-only private knowledge: visible only to the owner (never to the person it is about), stored in the owner’s Ideaflow people overlay and shown in Unlinked (person and contact pages) and OpenChat. Never notifies anyone. It is NOT a connection request and NOT messaging.",
        "operationId": "unlinked_list_private_things",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 120
            }
          },
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "person",
                "company",
                "idea",
                "project"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Owner-only private result (visibility owner_private)"
          },
          "400": {
            "description": "invalid_input (typed error body; overlayCode for overlay refusals)"
          },
          "401": {
            "description": "not_linked or grant_revoked"
          },
          "403": {
            "description": "scope_not_granted: Private people notes & relations is off for this key, or not consented for this OAuth app"
          },
          "404": {
            "description": "not_found"
          },
          "409": {
            "description": "ambiguous_name (with candidates) or identity_unavailable"
          },
          "429": {
            "description": "rate_limited"
          },
          "503": {
            "description": "upstream_unavailable"
          }
        }
      },
      "post": {
        "summary": "unlinked_save_private_thing: Find or save a private person, company, idea or project by name, for someone with no Unlinked profile or imported contact (“remember Maya from dinner”).",
        "description": "Find or save a private person, company, idea or project by name, for someone with no Unlinked profile or imported contact (“remember Maya from dinner”). Returns its id for notes and links. A name is reused only when it names exactly one saved thing; when several share it the call fails with code ambiguous_name and candidates — ask the user which one. Set createNew true with a clientRequestId only to save a different one with an existing name; retries return the same entity. Owner-only private knowledge: visible only to the owner (never to the person it is about), stored in the owner’s Ideaflow people overlay and shown in Unlinked (person and contact pages) and OpenChat. Never notifies anyone. It is NOT a connection request and NOT messaging.",
        "operationId": "unlinked_save_private_thing",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "person",
                      "company",
                      "idea",
                      "project"
                    ]
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "createNew": {
                    "type": "boolean"
                  },
                  "clientRequestId": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  }
                },
                "required": [
                  "kind",
                  "name"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Owner-only private result (visibility owner_private)"
          },
          "400": {
            "description": "invalid_input (typed error body; overlayCode for overlay refusals)"
          },
          "401": {
            "description": "not_linked or grant_revoked"
          },
          "403": {
            "description": "scope_not_granted: Private people notes & relations is off for this key, or not consented for this OAuth app"
          },
          "404": {
            "description": "not_found"
          },
          "409": {
            "description": "ambiguous_name (with candidates) or identity_unavailable"
          },
          "429": {
            "description": "rate_limited"
          },
          "503": {
            "description": "upstream_unavailable"
          }
        }
      }
    },
    "/api/agent/v1/private/search": {
      "get": {
        "summary": "unlinked_search_private: Search the owner’s private people knowledge in one call: entities matched by name or private note text, and relations matched by relation text, either end’s name, or relationType (knows, family, works_at, worked_with, works_on, attended, interested_in, other).",
        "description": "Search the owner’s private people knowledge in one call: entities matched by name or private note text, and relations matched by relation text, either end’s name, or relationType (knows, family, works_at, worked_with, works_on, attended, interested_in, other). Give at least one of query, relationType or kind. At most 50 of each; truncated says more matched. Notes and relations carry author (“owner” or “agent:<client>”), source (“app”, “connector”, “direct-key” or “suggestion”) and assertion (“stated” or “inferred”); null on records made before provenance was kept. Relations also carry relationType. Owner-only private knowledge: visible only to the owner (never to the person it is about), stored in the owner’s Ideaflow people overlay and shown in Unlinked (person and contact pages) and OpenChat. Never notifies anyone. It is NOT a connection request and NOT messaging.",
        "operationId": "unlinked_search_private",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 120
            }
          },
          {
            "name": "relationType",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "knows",
                "family",
                "works_at",
                "worked_with",
                "works_on",
                "attended",
                "interested_in",
                "other"
              ]
            }
          },
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "person",
                "company",
                "idea",
                "project"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Owner-only private result (visibility owner_private)"
          },
          "400": {
            "description": "invalid_input (typed error body; overlayCode for overlay refusals)"
          },
          "401": {
            "description": "not_linked or grant_revoked"
          },
          "403": {
            "description": "scope_not_granted: Private people notes & relations is off for this key, or not consented for this OAuth app"
          },
          "404": {
            "description": "not_found"
          },
          "409": {
            "description": "ambiguous_name (with candidates) or identity_unavailable"
          },
          "429": {
            "description": "rate_limited"
          },
          "503": {
            "description": "upstream_unavailable"
          }
        }
      }
    },
    "/api/agent/v1/private/neighbourhood": {
      "get": {
        "summary": "unlinked_get_neighbourhood: Read one person or thing and what surrounds it in the owner’s private graph: everything one (default) or two private relations away, and those relations (fromId/toId).",
        "description": "Read one person or thing and what surrounds it in the owner’s private graph: everything one (default) or two private relations away, and those relations (fromId/toId). subjectKind “unlinked” = an Unlinked person: a published profile id, a connection id from unlinked_list_connections (an imported LinkedIn contact) or a linkedin.com/in/ address (the same person is one entity whichever you give); “thing” = one of the owner’s saved people, companies, ideas or projects (its id); “user” = an OpenChat person (user id) the owner already has private notes about. Never creates anything; empty when nothing is recorded. Bounded; truncated says it was cut. Notes and relations carry author (“owner” or “agent:<client>”), source (“app”, “connector”, “direct-key” or “suggestion”) and assertion (“stated” or “inferred”); null on records made before provenance was kept. Relations also carry relationType. Owner-only private knowledge: visible only to the owner (never to the person it is about), stored in the owner’s Ideaflow people overlay and shown in Unlinked (person and contact pages) and OpenChat. Never notifies anyone. It is NOT a connection request and NOT messaging.",
        "operationId": "unlinked_get_neighbourhood",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "parameters": [
          {
            "name": "subjectKind",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "unlinked",
                "thing",
                "user"
              ]
            }
          },
          {
            "name": "subjectId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 512
            }
          },
          {
            "name": "depth",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "number",
                  "enum": [
                    1
                  ]
                },
                {
                  "type": "number",
                  "enum": [
                    2
                  ]
                }
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Owner-only private result (visibility owner_private)"
          },
          "400": {
            "description": "invalid_input (typed error body; overlayCode for overlay refusals)"
          },
          "401": {
            "description": "not_linked or grant_revoked"
          },
          "403": {
            "description": "scope_not_granted: Private people notes & relations is off for this key, or not consented for this OAuth app"
          },
          "404": {
            "description": "not_found"
          },
          "409": {
            "description": "ambiguous_name (with candidates) or identity_unavailable"
          },
          "429": {
            "description": "rate_limited"
          },
          "503": {
            "description": "upstream_unavailable"
          }
        }
      }
    },
    "/api/agent/v1/private/things/delete": {
      "post": {
        "summary": "unlinked_delete_private_thing: Delete (undo) one of the owner’s private entities by id: removes it with its private notes and every private relation to or from it.",
        "description": "Delete (undo) one of the owner’s private entities by id: removes it with its private notes and every private relation to or from it. Cannot be reversed. Deleting an entity about an Unlinked person forgets only the owner’s private notes; the profile and contact are untouched. Owner-only private knowledge: visible only to the owner (never to the person it is about), stored in the owner’s Ideaflow people overlay and shown in Unlinked (person and contact pages) and OpenChat. Never notifies anyone. It is NOT a connection request and NOT messaging.",
        "operationId": "unlinked_delete_private_thing",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "thingId": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64
                  }
                },
                "required": [
                  "thingId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Owner-only private result (visibility owner_private)"
          },
          "400": {
            "description": "invalid_input (typed error body; overlayCode for overlay refusals)"
          },
          "401": {
            "description": "not_linked or grant_revoked"
          },
          "403": {
            "description": "scope_not_granted: Private people notes & relations is off for this key, or not consented for this OAuth app"
          },
          "404": {
            "description": "not_found"
          },
          "409": {
            "description": "ambiguous_name (with candidates) or identity_unavailable"
          },
          "429": {
            "description": "rate_limited"
          },
          "503": {
            "description": "upstream_unavailable"
          }
        }
      }
    },
    "/api/agent/v1/private/notes": {
      "post": {
        "summary": "unlinked_add_private_note: Add a private note about a person or thing.",
        "description": "Add a private note about a person or thing. subjectKind “unlinked” = an Unlinked person: a published profile id, a connection id from unlinked_list_connections (an imported LinkedIn contact) or a linkedin.com/in/ address (the same person is one entity whichever you give); “thing” = one of the owner’s saved people, companies, ideas or projects (its id); “user” = an OpenChat person (user id) the owner already has private notes about. Adding the same text again returns the existing note. Recorded as written by this agent (author agent:<key or app name>, source direct-key). Set assertion “inferred” when you concluded it yourself. Owner-only private knowledge: visible only to the owner (never to the person it is about), stored in the owner’s Ideaflow people overlay and shown in Unlinked (person and contact pages) and OpenChat. Never notifies anyone. It is NOT a connection request and NOT messaging.",
        "operationId": "unlinked_add_private_note",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subjectKind": {
                    "type": "string",
                    "enum": [
                      "unlinked",
                      "thing",
                      "user"
                    ]
                  },
                  "subjectId": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  },
                  "text": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 4000
                  },
                  "assertion": {
                    "type": "string",
                    "enum": [
                      "stated",
                      "inferred"
                    ]
                  }
                },
                "required": [
                  "subjectKind",
                  "subjectId",
                  "text"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Owner-only private result (visibility owner_private)"
          },
          "400": {
            "description": "invalid_input (typed error body; overlayCode for overlay refusals)"
          },
          "401": {
            "description": "not_linked or grant_revoked"
          },
          "403": {
            "description": "scope_not_granted: Private people notes & relations is off for this key, or not consented for this OAuth app"
          },
          "404": {
            "description": "not_found"
          },
          "409": {
            "description": "ambiguous_name (with candidates) or identity_unavailable"
          },
          "429": {
            "description": "rate_limited"
          },
          "503": {
            "description": "upstream_unavailable"
          }
        }
      }
    },
    "/api/agent/v1/private/notes/delete": {
      "post": {
        "summary": "unlinked_delete_private_note: Delete (undo) one of the owner’s private notes by note id.",
        "description": "Delete (undo) one of the owner’s private notes by note id. Owner-only private knowledge: visible only to the owner (never to the person it is about), stored in the owner’s Ideaflow people overlay and shown in Unlinked (person and contact pages) and OpenChat. Never notifies anyone. It is NOT a connection request and NOT messaging.",
        "operationId": "unlinked_delete_private_note",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "noteId": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64
                  }
                },
                "required": [
                  "noteId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Owner-only private result (visibility owner_private)"
          },
          "400": {
            "description": "invalid_input (typed error body; overlayCode for overlay refusals)"
          },
          "401": {
            "description": "not_linked or grant_revoked"
          },
          "403": {
            "description": "scope_not_granted: Private people notes & relations is off for this key, or not consented for this OAuth app"
          },
          "404": {
            "description": "not_found"
          },
          "409": {
            "description": "ambiguous_name (with candidates) or identity_unavailable"
          },
          "429": {
            "description": "rate_limited"
          },
          "503": {
            "description": "upstream_unavailable"
          }
        }
      }
    },
    "/api/agent/v1/private/links": {
      "post": {
        "summary": "unlinked_add_private_link: Record a private relation in the owner’s own words (“knows”, “sister of”, “worked with”, “works at”), for example that one imported contact knows another.",
        "description": "Record a private relation in the owner’s own words (“knows”, “sister of”, “worked with”, “works at”), for example that one imported contact knows another. Subject: subjectKind “unlinked” = an Unlinked person: a published profile id, a connection id from unlinked_list_connections (an imported LinkedIn contact) or a linkedin.com/in/ address (the same person is one entity whichever you give); “thing” = one of the owner’s saved people, companies, ideas or projects (its id); “user” = an OpenChat person (user id) the owner already has private notes about. Target: toKind “unlinked” + toId (published profile id, connection id or linkedin.com/in/ address), toKind “user” + toId (OpenChat user id already in the owner’s private notes), or toKind person/company/idea/project with toId (saved thing id) or toName. A name is reused only when it names exactly one saved thing; when several share it the call fails with code ambiguous_name and candidates — ask the user which one, then pass its id. Set createNew true with a clientRequestId only to save a different one with an existing name. The same subject, relation and target is one link, so retries never duplicate. Recorded as written by this agent (author agent:<key or app name>, source direct-key). relationType is derived from the words. Owner-only private knowledge: visible only to the owner (never to the person it is about), stored in the owner’s Ideaflow people overlay and shown in Unlinked (person and contact pages) and OpenChat. Never notifies anyone. It is NOT a connection request and NOT messaging.",
        "operationId": "unlinked_add_private_link",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subjectKind": {
                    "type": "string",
                    "enum": [
                      "unlinked",
                      "thing",
                      "user"
                    ]
                  },
                  "subjectId": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  },
                  "relation": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 60
                  },
                  "toKind": {
                    "type": "string",
                    "enum": [
                      "unlinked",
                      "user",
                      "person",
                      "company",
                      "idea",
                      "project"
                    ]
                  },
                  "toId": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  },
                  "toName": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "createNew": {
                    "type": "boolean"
                  },
                  "clientRequestId": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "assertion": {
                    "type": "string",
                    "enum": [
                      "stated",
                      "inferred"
                    ]
                  }
                },
                "required": [
                  "subjectKind",
                  "subjectId",
                  "relation",
                  "toKind"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Owner-only private result (visibility owner_private)"
          },
          "400": {
            "description": "invalid_input (typed error body; overlayCode for overlay refusals)"
          },
          "401": {
            "description": "not_linked or grant_revoked"
          },
          "403": {
            "description": "scope_not_granted: Private people notes & relations is off for this key, or not consented for this OAuth app"
          },
          "404": {
            "description": "not_found"
          },
          "409": {
            "description": "ambiguous_name (with candidates) or identity_unavailable"
          },
          "429": {
            "description": "rate_limited"
          },
          "503": {
            "description": "upstream_unavailable"
          }
        }
      }
    },
    "/api/agent/v1/private/links/update": {
      "post": {
        "summary": "unlinked_update_private_link: Correct one of the owner’s private relations in place by link id, for example “sister of” to “cousin of”.",
        "description": "Correct one of the owner’s private relations in place by link id, for example “sister of” to “cousin of”. The relation text, its relationType and the link’s identity change together; if the owner already has that exact relation between the same two ends, the two become one and the existing link is returned with merged true. Recorded as written by this agent (author agent:<key or app name>, source direct-key). Owner-only private knowledge: visible only to the owner (never to the person it is about), stored in the owner’s Ideaflow people overlay and shown in Unlinked (person and contact pages) and OpenChat. Never notifies anyone. It is NOT a connection request and NOT messaging.",
        "operationId": "unlinked_update_private_link",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "linkId": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64
                  },
                  "relation": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 60
                  },
                  "assertion": {
                    "type": "string",
                    "enum": [
                      "stated",
                      "inferred"
                    ]
                  }
                },
                "required": [
                  "linkId",
                  "relation"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Owner-only private result (visibility owner_private)"
          },
          "400": {
            "description": "invalid_input (typed error body; overlayCode for overlay refusals)"
          },
          "401": {
            "description": "not_linked or grant_revoked"
          },
          "403": {
            "description": "scope_not_granted: Private people notes & relations is off for this key, or not consented for this OAuth app"
          },
          "404": {
            "description": "not_found"
          },
          "409": {
            "description": "ambiguous_name (with candidates) or identity_unavailable"
          },
          "429": {
            "description": "rate_limited"
          },
          "503": {
            "description": "upstream_unavailable"
          }
        }
      }
    },
    "/api/agent/v1/private/links/delete": {
      "post": {
        "summary": "unlinked_delete_private_link: Delete (undo) one of the owner’s private relations by link id.",
        "description": "Delete (undo) one of the owner’s private relations by link id. Owner-only private knowledge: visible only to the owner (never to the person it is about), stored in the owner’s Ideaflow people overlay and shown in Unlinked (person and contact pages) and OpenChat. Never notifies anyone. It is NOT a connection request and NOT messaging.",
        "operationId": "unlinked_delete_private_link",
        "security": [
          {
            "accountGrant": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "linkId": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64
                  }
                },
                "required": [
                  "linkId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Owner-only private result (visibility owner_private)"
          },
          "400": {
            "description": "invalid_input (typed error body; overlayCode for overlay refusals)"
          },
          "401": {
            "description": "not_linked or grant_revoked"
          },
          "403": {
            "description": "scope_not_granted: Private people notes & relations is off for this key, or not consented for this OAuth app"
          },
          "404": {
            "description": "not_found"
          },
          "409": {
            "description": "ambiguous_name (with candidates) or identity_unavailable"
          },
          "429": {
            "description": "rate_limited"
          },
          "503": {
            "description": "upstream_unavailable"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "accountGrant": {
        "type": "http",
        "scheme": "bearer"
      },
      "browserSession": {
        "type": "apiKey",
        "in": "cookie",
        "name": "__Host-ul-session"
      },
      "provisionClientBasic": {
        "type": "http",
        "scheme": "basic",
        "description": "Allow-listed confidential client id + secret (client_secret_basic); timing-safe verification"
      },
      "connectorOAuth": {
        "type": "oauth2",
        "description": "OAuth 2.1 authorization code with PKCE S256 for MCP connectors (public clients). The issued access token is an account grant: it does not expire and is revocable in Settings or at /oauth/revoke.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://www.unlinked.ai/oauth/authorize",
            "tokenUrl": "https://www.unlinked.ai/oauth/token",
            "scopes": {
              "network": "Read your own Unlinked account and network: who you are, the connections in your imported files and your linked profile’s recorded paths, and (where available) your connection requests and notifications.",
              "people": "Search and read the published People index on Unlinked (public professional profiles only).",
              "connections": "Send connection requests to members, and accept, ignore or withdraw your connection requests, under the same rules and daily limits as the site."
            }
          }
        }
      },
      "messagingService": {
        "type": "http",
        "scheme": "bearer",
        "description": "Dedicated OpenChat server credential; never a browser or MCP credential."
      }
    }
  },
  "externalDocs": {
    "description": "Shared Ideaflow connector for Unlinked, OpenChat and Thoughtstream Vision",
    "url": "https://id.ideaflow.app/agents"
  },
  "x-request-diagnostics": {
    "enabledByDefault": false,
    "responseHeader": "X-Request-ID",
    "idTrust": "server-generated; incoming trace IDs ignored",
    "privacy": "No credentials, queries, result bodies or sensitive URL parameters. Opaque account correlation only after successful authentication.",
    "gatewayLimitation": "Shared gateway does not currently forward the downstream Unlinked request ID."
  }
}
