Skip to content
Scalekit Docs
Talk to an Engineer Dashboard

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

  • An ACTIVE connected account for the user. Authorize a user 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: actions in Python, scalekit in Node.js. For cURL, get an access token and keep it in TOKEN:

    Terminal window
    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:

    ConnectorAPI reference
    GmailGmail API
    SlackSlack API methods
    GitHubGitHub REST API
    SalesforceSalesforce REST API
    HubSpotHubSpot API
  1. The proxy only works for an ACTIVE connected account. Check before you call, and send a new authorization link if it isn’t.

    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.")
  2. 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:

    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)

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

    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())

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

    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"))

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

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.

The proxy is off for that connector. Contact support@scalekit.com to turn it on.

authorization details are missing for connected account identifier

Section titled “authorization details are missing for connected account identifier”

No active connected account matches this identifier and connection. Check both values, and authorize the user if the account isn’t ACTIVE.

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

Section titled “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 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

Section titled “The app only accepts traffic from known IP addresses”

Add Scalekit’s outbound IP addresses for your region to the app’s allowlist.

API reference

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