> **Building with AI coding agents?** Install the authstack plugin with one command. This equips your agent with accurate Scalekit implementation patterns.
>
> **Recommended** (the `-y` flags skip prompts, so the command runs without an interactive terminal):
> ```bash
> npx -y @scalekit-inc/cli setup -y
> ```
>
> Global:
> ```bash
> npm install -g @scalekit-inc/cli
> scalekit setup -y
> ```
>
> Supports Claude Code, Cursor, GitHub Copilot, Codex + skills for other Agent Skills-compatible agents in `~/.agents/skills`.
> Skills: integrate-agentkit, implement-saaskit, add-mcp-oauth, implement-sso, implement-scim.
> [Full setup guide](https://docs.scalekit.com/dev-kit/build-with-ai/)

---

# Connect AI agents to LinkHut

The LinkHut connector lets your AI agent act in each user's LinkHut account. Each user signs in to LinkHut once, and Scalekit stores and refreshes their tokens, so your agent never handles credentials. It comes with 11 tools.

**Authentication:** OAuth 2.0
**Categories:** Productivity
**Tools:** 11: 7 read, 2 write, 2 destructive
**Users sign in with:** OAuth
**OAuth app:** Your own LinkHut app
**Built by:** Scalekit connector
**Try it:** [Playground in the Scalekit dashboard](https://app.scalekit.com/ws/signup?sk_intent=playground&provider=LINKHUT)

## Setup

1. ### Install the SDK

   **Node.js**

   ```bash
   npm install @scalekit-sdk/node dotenv
   ```

   **Python**

   ```bash
   pip install scalekit-sdk-python python-dotenv
   ```

2. ### Set your credentials

   Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**.

   ```sh title=".env"
   SCALEKIT_ENVIRONMENT_URL=<your-environment-url>
   SCALEKIT_CLIENT_ID=<your-client-id>
   SCALEKIT_CLIENT_SECRET=<your-client-secret>
   ```

3. ### Create the LinkHut connection

   In **AgentKit > Connections**, create a LinkHut connection and copy its redirect URI. The name you give it is the `connection_name` your code passes. See [Configure connections](/agentkit/connections/).

4. ### Register an OAuth app

   LinkHut connections use your own OAuth app. Register one with LinkHut and add the redirect URI you copied.

   Then enter the app's Client ID and Client Secret on the LinkHut connection.

5. ### Authorize a user and make your first call

   **Node.js** (`quickstart.mts`)

   ```typescript
   import { ScalekitClient } from '@scalekit-sdk/node'
   import 'dotenv/config'
   import { createInterface } from 'node:readline/promises'

   const scalekit = new ScalekitClient(
     process.env.SCALEKIT_ENVIRONMENT_URL,
     process.env.SCALEKIT_CLIENT_ID,
     process.env.SCALEKIT_CLIENT_SECRET,
   )
   const actions = scalekit.actions

   const connector = 'linkhut'
   const identifier = 'user_123'

   // Generate an authorization link for the user
   const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier })
   console.log('Authorize LinkHut:', link)
   const rl = createInterface({ input: process.stdin, output: process.stdout })
   await rl.question('Press Enter after authorizing...')
   rl.close()

   // Make your first call
   const result = await actions.executeTool({
     connector,
     identifier,
     toolName: 'linkhut_get_last_update',
     toolInput: {},
   })
   console.log(result)
   ```

   ```bash
   npx tsx quickstart.mts
   ```

   **Python** (`quickstart.py`)

   ```python
   import os
   from scalekit import ScalekitClient
   from dotenv import load_dotenv
   load_dotenv()

   scalekit_client = ScalekitClient(
       env_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"),
       client_id=os.getenv("SCALEKIT_CLIENT_ID"),
       client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"),
   )
   actions = scalekit_client.actions

   connection_name = "linkhut"
   identifier = "user_123"

   # Generate an authorization link for the user
   link_response = actions.get_authorization_link(
       connection_name=connection_name,
       identifier=identifier,
   )
   print("Authorize LinkHut:", link_response.link)
   input("Press Enter after authorizing...")

   # Make your first call
   result = actions.execute_tool(
       tool_input={},
       tool_name="linkhut_get_last_update",
       connection_name=connection_name,
       identifier=identifier,
   )
   print(result)
   ```

   ```bash
   python quickstart.py
   ```

   Each user signs in once. See [Authorize a user](/agentkit/tools/authorize/) for the full flow and statuses.

## Tools

Pass the exact name to `execute_tool`, with an input like each tool's example. To ask for a tool that's missing, use the [request form](https://scalekitsupport.portal.usepylon.com/forms/request-a-connector-tool).

**Node.js**

```typescript
const result = await actions.executeTool({
  toolName: 'linkhut_get_last_update',
  toolInput: {},
  connector: 'linkhut',
  identifier: 'user_123',
})
```

**Python**

```python
result = actions.execute_tool(
    tool_name="linkhut_get_last_update",
    tool_input={},
    connection_name="linkhut",
    identifier="user_123",
)
```

`result.data` is the app's response as JSON, and `result.execution_id` (`executionId` in Node.js) is the ID of the call.

### `linkhut_get_last_update`

Get Last Update · Read-only

Get the timestamp of the most recent bookmark change in the account.
Returns update_time as an ISO 8601 UTC string.
Use it to check whether bookmarks changed before calling linkhut_list_all_posts.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs: none.

Example input: `{}`

### `linkhut_get_posts`

Get Bookmarks · Read-only

Get bookmarks matching a date, URL, tag or URL hash filter.
Returns a posts list with href, description, extended notes, hash, tags, time, shared and toread. With no date or URL it uses the most recent day that has bookmarks.
Use for lookups of specific bookmarks. Use linkhut_list_recent_posts for the latest ones and linkhut_list_all_posts for everything.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `dt` (`string`, optional): Return bookmarks from this day; defaults to the most recent day with bookmarks.
- `hashes` (`string`, optional): MD5 hashes of URLs to fetch, separated by spaces, regardless of date.
- `meta` (`string`, optional): Set to yes to include a change-detection signature in each bookmark's meta field. One of: `yes`.
- `tag` (`string`, optional): Tags to filter by, separated by spaces for more than one, for example webdev dom.
- `url` (`string`, optional): Return the bookmark for this exact URL regardless of date.

Example input: `{}`

### `linkhut_list_all_posts`

List All Bookmarks · Read-only

List all bookmarks in the account with optional tag, date range and paging filters.
Returns an array of bookmarks with href, description, extended notes, hash, tags, time, shared and toread. Up to 1000 are returned by default.
Use sparingly: check linkhut_get_last_update first, and use linkhut_list_recent_posts for a few items.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `fromdt` (`string`, optional): Only return bookmarks saved on or after this time.
- `meta` (`string`, optional): Set to yes to include a change-detection signature in each bookmark's meta field. One of: `yes`.
- `results` (`integer`, optional): Maximum number of bookmarks to return; the server default is 1000.
- `start` (`integer`, optional): Number of results to skip before returning bookmarks.
- `tag` (`string`, optional): Only return bookmarks with this tag.
- `todt` (`string`, optional): Only return bookmarks saved on or before this time.

Example input: `{}`

### `linkhut_list_post_dates`

List Bookmark Dates · Read-only

List the dates on which bookmarks were saved with the count for each date.
Returns a dates object mapping YYYY-MM-DD to bookmark count.
Use to find which days have bookmarks before calling linkhut_get_posts with a date.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `tag` (`string`, optional): Only count bookmarks with this tag.

Example input: `{}`

### `linkhut_list_recent_posts`

List Recent Bookmarks · Read-only

List the most recently saved bookmarks, optionally filtered by tag.
Returns a posts list with href, description, extended notes, hash, tags, time, shared and toread.
Use for the latest bookmarks. Use linkhut_get_posts for a specific date or URL.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `count` (`integer`, optional): Number of bookmarks to return, from 1 to 100; the server default is 15.
- `tag` (`string`, optional): Only return bookmarks with this tag.

Example input: `{}`

### `linkhut_list_tags`

List Tags · Read-only

List all of the user's tags with how many bookmarks use each.
Returns an object mapping tag name to usage count.
Use to see existing tags before linkhut_rename_tag or linkhut_delete_tag.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs: none.

Example input: `{}`

### `linkhut_suggest_tags`

Suggest Tags · Read-only

Suggest tags for a URL from site-wide popular tags and the user's own tags.
Returns two lists, popular and recommended, of tag names.
Use before linkhut_add_post to choose tags for a new bookmark.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `url` (`string`, required): URL to get tag suggestions for.

Example input: `{"url":"<url>"}`

### `linkhut_add_post`

Add Bookmark · Write

Save a bookmark with a title, optional notes and tags.
Returns result_code, which is done on success.
Use to create or overwrite a bookmark for a URL. Use linkhut_delete_post to remove one.

Read only: no. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `description` (`string`, required): Title of the bookmark.
- `url` (`string`, required): URL to bookmark, for example https://sourcehut.org/.
- `dt` (`string`, optional): Creation time to record for the bookmark; defaults to now.
- `extended` (`string`, optional): Notes or long description for the bookmark.
- `replace` (`string`, optional): Set to no to keep the existing bookmark when the URL is already saved; the server replaces it by default. One of: `yes`, `no`.
- `shared` (`string`, optional): Set to no to make the bookmark private. One of: `yes`, `no`.
- `tags` (`string`, optional): Comma-delimited tags to attach, for example git,oss.
- `toread` (`string`, optional): Set to yes to mark the bookmark as unread. One of: `yes`, `no`.

Example input: `{"description":"<description>","url":"<url>"}`

### `linkhut_rename_tag`

Rename Tag · Write

Rename a tag across all bookmarks.
Returns result_code, which is done on success.
Use linkhut_list_tags first to find the current tag name.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `new` (`string`, required): New name for the tag.
- `old` (`string`, required): Current name of the tag to rename.

Example input: `{"new":"<new>","old":"<old>"}`

### `linkhut_delete_post`

Delete Bookmark · Destructive

Delete the bookmark saved for a URL.
Returns result_code, which is done on success.
Use linkhut_get_posts first to confirm the bookmark exists.

Read only: no. Destructive: yes. Idempotent (safe to retry): yes.

Inputs:

- `url` (`string`, required): URL of the bookmark to delete.

Example input: `{"url":"<url>"}`

### `linkhut_delete_tag`

Delete Tag · Destructive

Remove a tag from every bookmark that has it.
Returns result_code, which is done on success. The bookmarks themselves are kept.
Use linkhut_list_tags first to confirm the tag name. Use linkhut_rename_tag to keep the bookmarks grouped.

Read only: no. Destructive: yes. Idempotent (safe to retry): yes.

Inputs:

- `tag` (`string`, required): Name of the tag to remove from all bookmarks.

Example input: `{"tag":"<tag>"}`

## Related

Other Productivity connectors ([all 144](/agentkit/connectors/?category=productivity)):

- [Slack](/agentkit/connectors/slack/): Scalekit connector, OAuth, 96 tools
- [Microsoft 365](/agentkit/connectors/microsoft365/): Scalekit connector, OAuth, 317 tools
- [Notion](/agentkit/connectors/notion/): Scalekit connector, OAuth, 51 tools


---

## More Scalekit documentation

| Resource | What it contains | When to use it |
|----------|-----------------|----------------|
| [/llms.txt](/llms.txt) | Structured index with routing hints per product area | Start here — find which documentation set covers your topic before loading full content |
| [/llms-full.txt](/llms-full.txt) | Complete documentation for all Scalekit products in one file | Use when you need exhaustive context across multiple products or when the topic spans several areas |
| [sitemap-0.xml](https://docs.scalekit.com/sitemap-0.xml) | Full URL list of every documentation page | Use to discover specific page URLs you can fetch for targeted, page-level answers |
