> **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/)

---

# Call any API

Call any endpoint of a connector's API as the user through the Scalekit API proxy, then wrap it as a tool for your agent. Python, Node.js and cURL.
When no [built-in tool](/agentkit/tools/scalekit-optimized-tools/) does what you need, call the app's API directly through the Scalekit proxy. You send the path, method and body; Scalekit adds the user's credentials, calls the app at its base URL and returns the raw response. Then you can wrap the call as your own tool. The proxy is on for most connectors.

Search the built-in tools first: they come with schemas and structured output, and cover most of each app's API.

## Before you start

- An `ACTIVE` connected account for the user. [Authorize a user](/agentkit/tools/authorize/) shows how to get one.
- The connection name from **AgentKit** > **Connections**. These examples use a Slack connection named `slack`.
- A client set up as in the [Quickstart](/agentkit/quickstart/): `actions` in Python, `scalekit` in Node.js. For cURL, get an access token and keep it in `TOKEN`:

  ```bash
  TOKEN=$(curl -sS "$SCALEKIT_ENVIRONMENT_URL/oauth/token" \
    -d grant_type=client_credentials \
    -d client_id="$SCALEKIT_CLIENT_ID" \
    -d client_secret="$SCALEKIT_CLIENT_SECRET" | jq -r .access_token)
  ```
- The app's API reference for the endpoint you want:

  | Connector | API reference |
  | --- | --- |
  | Gmail | [Gmail API](https://developers.google.com/gmail/api/reference/rest) |
  | Slack | [Slack API methods](https://api.slack.com/methods) |
  | GitHub | [GitHub REST API](https://docs.github.com/en/rest) |
  | Salesforce | [Salesforce REST API](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/) |
  | HubSpot | [HubSpot API](https://developers.hubspot.com/docs/api/overview) |

1. ## Check the connected account is active

   The proxy only works for an `ACTIVE` connected account. Check before you call, and send a new authorization link if it isn't.

   **Python**

   ```python
   account = actions.get_connected_account_details(
       connection_name="slack",
       identifier="user_123",
   ).connected_account
   if account.status != "ACTIVE":
       raise SystemExit(f"Slack is {account.status}. Send the user a new authorization link.")
   ```

   **Node.js**

   ```ts
   import { ConnectorStatus } from '@scalekit-sdk/node';

   const { connectedAccount: account } = await scalekit.actions.getConnectedAccount({
     connectionName: 'slack',
     identifier: 'user_123',
   });
   if (account?.status !== ConnectorStatus.ACTIVE) {
     throw new Error('Slack is not ACTIVE. Send the user a new authorization link.');
   }
   ```

   **cURL**

   ```bash
   curl -sS -G "$SCALEKIT_ENVIRONMENT_URL/api/v1/connected_accounts/details" \
     -H "Authorization: Bearer $TOKEN" \
     --data-urlencode "connector=slack" \
     --data-urlencode "identifier=user_123" |
     jq -r '.connected_account.status'
   ```

2. ## Call the endpoint

   Pass the path from the app's API reference, without the base URL. Query parameters go in `query_params`, and a JSON body goes in `body`. This reads the custom profile fields of the user's Slack workspace, which no built-in Slack tool covers:

   **Python**

   ```python
   from scalekit.common.exceptions import ScalekitServerException

   try:
       response = actions.request(
           connection_name="slack",
           identifier="user_123",
           method="GET",
           path="/api/team.profile.get",
           query_params={"visibility": "visible"},
       )
       print(response.status_code, response.json())
   except ScalekitServerException as e:
       print("Request failed:", e.error_code, e.message)
   ```

   **Node.js**

   ```ts
   import { ScalekitServerException } from '@scalekit-sdk/node';

   try {
     const response = await scalekit.actions.request({
       connectionName: 'slack',
       identifier: 'user_123',
       method: 'GET',
       path: '/api/team.profile.get',
       queryParams: { visibility: 'visible' },
     });
     console.log(response.status, response.data);
   } catch (err) {
     if (!(err instanceof ScalekitServerException)) throw err;
     console.error('Request failed:', err.errorCode, err.message);
   }
   ```

   **cURL**

   ```bash
   curl -sS -G "$SCALEKIT_ENVIRONMENT_URL/proxy/api/team.profile.get" \
     -H "Authorization: Bearer $TOKEN" \
     -H "connection_name: slack" \
     -H "identifier: user_123" \
     --data-urlencode "visibility=visible"
   ```

   A write works the same way with a body. This sets a field on the user's Slack profile:

   **Python**

   ```python
   response = actions.request(
       connection_name="slack",
       identifier="user_123",
       method="POST",
       path="/api/users.profile.set",
       body={"profile": {"title": "Support engineer"}},
   )
   print(response.status_code, response.json())
   ```

   **Node.js**

   ```ts
   const response = await scalekit.actions.request({
     connectionName: 'slack',
     identifier: 'user_123',
     method: 'POST',
     path: '/api/users.profile.set',
     body: { profile: { title: 'Support engineer' } },
   });
   console.log(response.status, response.data);
   ```

   **cURL**

   ```bash
   curl -sS -X POST "$SCALEKIT_ENVIRONMENT_URL/proxy/api/users.profile.set" \
     -H "Authorization: Bearer $TOKEN" \
     -H "connection_name: slack" \
     -H "identifier: user_123" \
     -H "Content-Type: application/json" \
     -d '{"profile": {"title": "Support engineer"}}'
   ```

   The proxy returns the app's response as is: its status code, headers and body. In Python, `request` returns a `requests` response (`status_code`, `json()`) for every status, so check `status_code`. In Node.js, it returns an Axios response (`status`, `data`) for a `2xx` status, and throws a `ScalekitServerException` for any other status, so wrap the call in `try`/`catch`.

3. ## Wrap it as a tool

   Give your model a tool that matches what the agent needs to do, not the shape of the app's endpoint. Keep the input schema small, validate model input before you send it, and return only the fields the model needs.

   **Python**

   ```python
   list_profile_fields_tool = {
       "name": "slack_list_profile_fields",
       "description": "List the custom profile fields defined in the user's Slack workspace, "
       "such as Title or Pronouns. Returns each field's ID, label and type.",
       "input_schema": {"type": "object", "properties": {}},
   }

   def slack_list_profile_fields(identifier: str) -> dict:
       response = actions.request(
           connection_name="slack",
           identifier=identifier,
           method="GET",
           path="/api/team.profile.get",
       )
       data = response.json()
       if not data.get("ok"):
           return {"error": data.get("error")}
       fields = data.get("profile", {}).get("fields", [])
       return {"fields": [{"id": f["id"], "label": f["label"], "type": f["type"]} for f in fields]}

   print(slack_list_profile_fields("user_123"))
   ```

   **Node.js**

   ```ts
   const listProfileFieldsTool = {
     name: 'slack_list_profile_fields',
     description:
       "List the custom profile fields defined in the user's Slack workspace, " +
       "such as Title or Pronouns. Returns each field's ID, label and type.",
     input_schema: { type: 'object', properties: {} },
   };

   type ProfileField = { id: string; label: string; type: string };

   async function slackListProfileFields(identifier: string) {
     const response = await scalekit.actions.request({
       connectionName: 'slack',
       identifier,
       method: 'GET',
       path: '/api/team.profile.get',
     });
     const data = response.data as { ok: boolean; error?: string; profile?: { fields?: ProfileField[] } };
     if (!data.ok) return { error: data.error };
     const fields = data.profile?.fields ?? [];
     return { fields: fields.map(({ id, label, type }) => ({ id, label, type })) };
   }

   console.log(listProfileFieldsTool.name, await slackListProfileFields('user_123'));
   ```

   Pass the tool definition to your model with the built-in tools, and run the function when the model calls it.

## Check it worked

The call returns the app's own response. For Slack, a `200` with `"ok": true` and the fields you asked for. Slack reports errors in the body with `"ok": false`, while most other APIs use a `4xx` status.

## Common problems

### `proxy not enabled for provider`

The proxy is off for that connector. Contact [support@scalekit.com](mailto:support@scalekit.com) to turn it on.

### `authorization details are missing for connected account identifier`

No active connected account matches this identifier and connection. Check both values, and [authorize the user](/agentkit/tools/authorize/) if the account isn't `ACTIVE`.

### Node.js: `request()` throws a `401` on the first call

Make another SDK call before your first `request()`, such as the account check in step 1. The Node.js client fetches its access token on its first regular SDK call, and `request()` then sends that token. In a long-running process, if `request()` later throws a `401` because the token expired, make another SDK call and retry.

### The app returns `401` or `403`

The user's token is missing a scope the endpoint needs. Add the scope to the connection, then have the user authorize again.

### The app only accepts traffic from known IP addresses

Add Scalekit's [outbound IP addresses](/reference/outbound-ip-addresses/) for your region to the app's allowlist.

## Next

  - [Choose a framework](/agentkit/examples/): Add your tools to an agent in LangChain, OpenAI, Anthropic and more.
  - [Create a connector](/agentkit/bring-your-own-connector/overview/): Add an app that isn't in the catalog.

## API reference

The endpoints this page's code calls, with every field and the SDK method for each:

- `GET` [Get a connected account](https://docs.scalekit.com/agentkit/reference/connected-accounts/get-a-connected-account.md)
- `GET` [Get a connected account's credentials](https://docs.scalekit.com/agentkit/reference/connected-accounts/get-connected-account-credentials.md)


---

## 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 |
