Client libraries
If your agent already uses a vendor's client library, keep it. Give it the run's address and token instead of the real service's, and nothing else in your code changes. This page lists the one setting each library needs.
At a glance
Each library below was tested against a run served over HTTPS exactly as hosted runs are, with reads and writes: queries, lists across several pages, creates and updates.
| App | Library | Tested | What to set |
|---|---|---|---|
| Slack | slack_sdk (Python) | 3.46.0 | base_url |
| Slack | @slack/web-api (Node) | 8.3.0 | slackApiUrl |
google-api-python-client | 2.201.0 |
discoveryServiceUrl and static_discovery=False | |
googleapis (Node) | 189.0.0 | rootUrl | |
| Salesforce | simple-salesforce | 1.12.10 | instance_url,
version |
| Salesforce | jsforce (Node) | 3.13.1 | instanceUrl,
version |
| Shopify | ShopifyAPI (Python) | 12.7.0 |
Session.myshopify_domain. GraphQL only. |
| Shopify | @shopify/admin-api-client | 2.0.1 | storeDomain |
| Zendesk | zenpy | 2.0.57 | subdomain and
domain |
| Zendesk | node-zendesk | 6.0.1 | endpointUri |
The examples read the run as in Apps and credentials: app(name) is a run's app
and credential(name, kind) one of its tokens. In Node:
const run = JSON.parse(require("fs").readFileSync(process.env.STATEWEAVE_RUN_FILE, "utf8"));
const app = (name) => run.apps[name];
const credential = (name, kind = "user") =>
Object.entries(app(name).credentials).find(([label]) => label.split(":")[0] === kind)[1];Slack
from slack_sdk import WebClient
slack = WebClient(token=credential("slack", "user"), base_url=app("slack")["endpoints"]["web_api"])const { WebClient } = require("@slack/web-api");
const slack = new WebClient(credential("slack", "user"), { slackApiUrl: app("slack").endpoints.web_api });The same setting serves Gmail, Calendar, Sheets and Drive. Python's library ships its own copy of each API
description, so turn that off and point it at the run's. Node's takes the run's address as rootUrl,
with a trailing slash.
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
gmail = build("gmail", "v1", credentials=Credentials(credential("gmail")),
discoveryServiceUrl=app("gmail")["endpoints"]["discovery"], static_discovery=False)const { google } = require("googleapis");
const auth = new google.auth.OAuth2();
auth.setCredentials({ access_token: credential("gmail") });
const gmail = google.gmail({ version: "v1", auth, rootUrl: app("gmail").base_url + "/" });For Sheets, list spreadsheets through Drive with the Sheets app's own token and its drive_discovery
(Python) or the same rootUrl (Node).
Salesforce
Pass the version the run's rest_api names (68.0), not the library's default.
from simple_salesforce import Salesforce
sf = Salesforce(instance_url=app("salesforce")["endpoints"]["instance_url"],
session_id=credential("salesforce"), version="68.0")
accounts = sf.query("SELECT Id, Name FROM Account LIMIT 10")["records"]const jsforce = require("jsforce");
const conn = new jsforce.Connection({
instanceUrl: app("salesforce").endpoints.instance_url,
accessToken: credential("salesforce"),
version: "68.0",
});
const accounts = (await conn.query("SELECT Id, Name FROM Account LIMIT 10")).records;Shopify
StateWeave's Shopify serves the GraphQL Admin API. The REST Admin API (paths such as
/admin/api/2026-10/products.json) answers 404 with the header x-twin-not-implemented: 1, so
use a library's GraphQL client.
import shopify
host = app("shopify")["base_url"].removeprefix("https://") # t-….app.stateweave.net
shop, _, domain = host.partition(".")
shopify.Session.myshopify_domain = domain # the library appends this to the shop's name
session = shopify.Session(shop, "2026-10", credential("shopify", "app"))
shopify.ShopifyResource.activate_session(session)
result = shopify.GraphQL().execute("{ shop { name } orders(first: 10) { nodes { name } } }")const { createAdminApiClient } = require("@shopify/admin-api-client");
const shopify = createAdminApiClient({
storeDomain: app("shopify").base_url.replace("https://", ""),
apiVersion: "2026-10",
accessToken: credential("shopify", "app"),
});
const { data } = await shopify.request("{ shop { name } orders(first: 10) { nodes { name } } }");ShopifyAPI builds its own address. Given any shop name, it keeps the first label and adds
.myshopify.com. Without Session.myshopify_domain set, your agent would call a real Shopify
store, not the run. Its REST resources (shopify.Product, shopify.Order, …) use the REST
Admin API and fail with ResourceNotFound.
Zendesk
from zenpy import Zenpy
host = app("zendesk")["base_url"].removeprefix("https://") # t-….app.stateweave.net
subdomain, _, domain = host.partition(".")
zendesk = Zenpy(subdomain=subdomain, domain=domain, oauth_token=credential("zendesk"))const zendesk = require("node-zendesk");
const client = zendesk.createClient({
endpointUri: app("zendesk").endpoints.rest_api.replace(/\/$/, ""),
token: credential("zendesk"),
oauth: true,
});Other libraries
Any library works if it lets you set where it sends requests and which token it uses. Watch for two things:
- A hard-coded host. Some libraries build the address from an account or shop name, like ShopifyAPI above. Find the setting that changes the domain; without one, the library can't reach the run.
- Its own sign-in. A run hands over access tokens, not client secrets or refresh tokens. Give the library the token directly instead of running its OAuth flow.
Tell us at mikewei@stateweave.net if a library you use doesn't work.