# Scalekit AgentKit API reference > Every AgentKit REST endpoint and webhook, with parameters, response fields, errors, a cURL request and the Python and Node.js SDK method that calls it, after the pages on authentication, errors and rate limits, and pagination. The guides are in https://docs.scalekit.com/_llms-txt/agentkit.txt, and each connector's tools are in https://docs.scalekit.com/_llms-txt/agentkit-connectors.txt. --- Source: https://docs.scalekit.com/agentkit/reference.md # AgentKit API reference AgentKit's API lets your agent act in each user's apps. It sends the user to approve access, keeps their tokens, and runs tools as that user. The API is REST over HTTPS. Requests and responses are JSON, and every request carries a bearer token. Call it with the Python or Node.js SDK, or from any language over HTTP. Each endpoint page shows the REST request and the SDK methods that call it. A [connector](https://docs.scalekit.com/agentkit/concepts/) is the app, such as Gmail, and a connection is your configuration for it, so the REST field `connector` takes the connection name, such as `my-gmail`. ## Start here 1. [Authenticate](https://docs.scalekit.com/agentkit/reference/authentication/) with your client ID and secret. 2. Create a user's [authorization link](https://docs.scalekit.com/agentkit/reference/authorization/get-an-authorization-link/) and send them to approve. 3. [Execute a tool](https://docs.scalekit.com/agentkit/reference/tools/execute-a-tool/) as that user. For a working agent end to end, follow the [Quickstart](https://docs.scalekit.com/agentkit/quickstart/). To use AgentKit with LangChain, Google ADK, the Vercel AI SDK or another framework, [choose a framework](https://docs.scalekit.com/agentkit/examples/). ## Resources - [Connections](https://docs.scalekit.com/agentkit/reference/connections.md): Your settings for one app, such as Gmail: create, read, update and delete it. - [Connected accounts](https://docs.scalekit.com/agentkit/reference/connected-accounts.md): Each user's link to an app: create, read, update and delete it. - [Authorization](https://docs.scalekit.com/agentkit/reference/authorization.md): Send a user to approve access, then confirm it was them. - [Tools](https://docs.scalekit.com/agentkit/reference/tools.md): Find the tools a user can call, and run one as that user. - [Virtual MCP servers](https://docs.scalekit.com/agentkit/reference/virtual-mcp-servers.md): Serve a chosen set of tools to an MCP client, with a token per user. - [Custom connectors](https://docs.scalekit.com/agentkit/reference/custom-connectors.md): Tell Scalekit how to sign in to an app that isn't in the catalog. - [Triggers](https://docs.scalekit.com/agentkit/reference/triggers.md): Get an event when something changes in a user's app, and check its delivery. - [Webhooks](https://docs.scalekit.com/agentkit/reference/events.md): The events Scalekit sends to your endpoint when a connected account changes. ## Concepts - [Authentication](https://docs.scalekit.com/agentkit/reference/authentication.md): Authenticate to the AgentKit API with the OAuth 2.0 client credentials grant: get your credentials, request an access token and send it as a bearer token. - [Errors and rate limits](https://docs.scalekit.com/agentkit/reference/errors.md): AgentKit API errors: the error body, how to tell a Scalekit error from the app's, what each tool error code means, and how to handle them and rate limits. - [Pagination](https://docs.scalekit.com/agentkit/reference/pagination.md): Page through AgentKit list endpoints with page_size and page_token: which endpoints page, their page size limits, and a loop in cURL, Python and Node.js. ## Examples ### Base URL ```text https://.scalekit.dev https://.scalekit.com ``` Development environments end in `.scalekit.dev`, Production in `.scalekit.com`. Copy yours from **Developers** > **Settings** > **API Credentials**. See [Environments and regions](https://docs.scalekit.com/agentkit/environments/). ### SDKs Python: ```bash pip install scalekit-sdk-python ``` Node.js: ```bash npm install @scalekit-sdk/node ``` Source and releases: [scalekit-sdk-python](https://github.com/scalekit-inc/scalekit-sdk-python), [scalekit-sdk-node](https://github.com/scalekit-inc/scalekit-sdk-node). ### Create a client REST: ```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) ``` Python: ```python import os from scalekit import ScalekitClient scalekit_client = ScalekitClient( env_url=os.environ["SCALEKIT_ENVIRONMENT_URL"], client_id=os.environ["SCALEKIT_CLIENT_ID"], client_secret=os.environ["SCALEKIT_CLIENT_SECRET"], ) actions = scalekit_client.actions ``` Node.js: ```ts import { ScalekitClient } from '@scalekit-sdk/node'; const scalekit = new ScalekitClient( process.env.SCALEKIT_ENVIRONMENT_URL!, process.env.SCALEKIT_CLIENT_ID!, process.env.SCALEKIT_CLIENT_SECRET!, ); const actions = scalekit.actions; ``` Next: [Authentication](https://docs.scalekit.com/agentkit/reference/authentication.md) --- Source: https://docs.scalekit.com/agentkit/reference/authentication.md # Authentication AgentKit uses the OAuth 2.0 client credentials grant. Exchange your client ID and secret for an access token, then send the token on every request: `Authorization: Bearer `. ## Get your credentials Each environment has its own environment URL, client ID and client secret, under **Developers** > **Settings** > **API Credentials**. See [API credentials](https://docs.scalekit.com/agentkit/api-credentials/) to generate and rotate a secret. ## Request a token POST to `/oauth/token` on your environment URL with `grant_type=client_credentials`. The response has `access_token`, `token_type` and `expires_in`, in seconds. The SDK clients get the token for you. ## When a call returns 401 Read `error_code` in the error body to tell the two causes apart: - **`UNAUTHENTICATED`**: your access token is missing, invalid or expired. Get a new token and retry. The SDK clients do this for you. - **`TOOL_ERROR`** with `tool_error_code` `REAUTHENTICATION_NEEDED`, or `UNAUTHENTICATED` when the connected account is `EXPIRED`: the app rejected the user's credentials and Scalekit couldn't refresh them, for example because access was revoked or a stored API key was rejected. A new token won't help: the user must authorize again, so send them a new [authorization link](https://docs.scalekit.com/agentkit/reference/authorization/get-an-authorization-link/). See [Errors and rate limits](https://docs.scalekit.com/agentkit/reference/errors/). ## Keep the secret on your server Call the API from your backend only. Never put the client secret in browser or mobile code, or in an agent's prompt or tool output. ## Acting as a user The token identifies your environment, not an end user. Endpoints that act for a user take `identifier`, your ID for that user, or a `connected_account_id`. See [Connected accounts](https://docs.scalekit.com/agentkit/reference/connected-accounts/). ## Examples ### Request a token REST: ```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) ``` Python: ```python import os from scalekit import ScalekitClient scalekit_client = ScalekitClient( env_url=os.environ["SCALEKIT_ENVIRONMENT_URL"], client_id=os.environ["SCALEKIT_CLIENT_ID"], client_secret=os.environ["SCALEKIT_CLIENT_SECRET"], ) actions = scalekit_client.actions ``` Node.js: ```ts import { ScalekitClient } from '@scalekit-sdk/node'; const scalekit = new ScalekitClient( process.env.SCALEKIT_ENVIRONMENT_URL!, process.env.SCALEKIT_CLIENT_ID!, process.env.SCALEKIT_CLIENT_SECRET!, ); const actions = scalekit.actions; ``` ### Use it ```bash curl -sS "$SCALEKIT_ENVIRONMENT_URL/api/v1/tools" \ -H "Authorization: Bearer $TOKEN" ``` Next: [Errors and rate limits](https://docs.scalekit.com/agentkit/reference/errors.md) --- Source: https://docs.scalekit.com/agentkit/reference/errors.md # Errors and rate limits An error returns an HTTP status and a JSON body. `code` is the gRPC status code and `message` says what went wrong. `details` holds an `ErrorInfo` whose `error_code` is a stable name to match on. Match on `error_code`, not on the message text. ## Scalekit or the app? A tool call can fail in Scalekit or in the app the tool calls, such as Gmail or Slack. `error_code` tells you which: | error_code | Who rejected the call | What to do | | --- | --- | --- | | `TOOL_ERROR` | The app. `tool_error_info` says why. | Read `tool_error_info.tool_error_code`, below. | | Anything else, such as `RESOURCE_NOT_FOUND`, `UNAUTHENTICATED`, `INVALID_ARGUMENT` | Scalekit, before the app was called. | Fix the request. Each endpoint page lists the errors it returns. | ## Tool errors `tool_error_info` has three fields: `tool_error_code`, `tool_error_message` (what the app returned) and `execution_id` (quote it to support). | tool_error_code | HTTP | Meaning | What to do | | --- | --- | --- | --- | | `REAUTHENTICATION_NEEDED`, `UNAUTHENTICATED` | 401 | The app rejected the user's credentials and Scalekit couldn't refresh them: access was revoked or expired, or the app rejected a stored API key or other credential. The connected account is `EXPIRED` | The user must authorize again: send them a new [authorization link](https://docs.scalekit.com/agentkit/reference/authorization/get-an-authorization-link/). | | `FORBIDDEN`, `PERMISSION_DENIED` | 403 | The user's account lacks the scope this tool needs. For OAuth, also returned when a refreshed token is still rejected; the account stays `ACTIVE` | Add the scope to the connection, then have the user authorize again. | | `RESOURCE_NOT_FOUND` | 404 | The app couldn't find what the call refers to, such as an event ID | Check the IDs in `params`. | | `RATE_LIMITED` | 429 | The app's rate limit or quota | Back off and retry. See [Rate limits](#rate-limits). | | `INVALID_ARGUMENT` | 400 | The app or the tool rejected the input, or a tool from an MCP server returned an error | Read `tool_error_message` and fix the parameters. | | `EXECUTION_ERROR` | 400 | The app returned an error for this call | Read `tool_error_message`, fix the parameters or the account, and retry. | | `INTERNAL_ERROR` | 400 or 500 | `400`: the app rejected the call. `500`: Scalekit couldn't run the tool | For `400`, read `tool_error_message`. For `500`, retry later, and quote `execution_id` to support if it persists. | | `TOOL_ERROR` | 400 or 500 | From a tool on an app's MCP server: `400` when the server rejected the call, `500` when it failed, rate-limited the call or couldn't be reached | For `400`, read `tool_error_message`. For `500`, back off before you retry. | ## Tools from an MCP server A tool from an app's own MCP server maps what the server returned to these codes. The server's status and message are in `tool_error_message`. - **`401`** with `REAUTHENTICATION_NEEDED`: the server rejected the user's credentials. The user must authorize again. - **`403`** with `FORBIDDEN`: the server refused the call. - **`400`** with `INVALID_ARGUMENT`: the tool returned an error result, or the server rejected the input. **`400`** with `TOOL_ERROR`: the server returned any other 4xx status. - **`500`** with `TOOL_ERROR`: the server returned `408`, `429` or a 5xx status, or Scalekit couldn't reach it in time. A retry can run the tool twice, so retry only tools that are safe to repeat. ## Rate limits When an app rate-limits a call, Scalekit returns HTTP 429 with `error_code` `TOOL_ERROR` and `tool_error_code` `RATE_LIMITED`. Each app sets its own limits, per user or per OAuth app. With [your own OAuth app](https://docs.scalekit.com/agentkit/advanced/bring-your-own-oauth/), you get your own quota at apps that count per app. The response has no `Retry-After` header. Retry with exponential backoff and jitter, for example 1, 2, then 4 seconds, and stop after a few tries. **Two exceptions.** A tool from an app's own MCP server returns HTTP 500 with `TOOL_ERROR` when the server rate-limits the call, as [Tools from an MCP server](#tools-from-an-mcp-server) describes. The [API proxy](https://docs.scalekit.com/agentkit/tools/custom-tools/) returns the app's status and headers unchanged, including its `Retry-After`. [Troubleshooting](https://docs.scalekit.com/agentkit/troubleshooting/) covers the errors users see while connecting. ## Examples ### Scalekit rejected it · HTTP 404 ```json { "code": 5, "message": "connector token not found for the given connector and environment id", "details": [ { "@type": "type.googleapis.com/scalekit.v1.errdetails.ErrorInfo", "error_code": "RESOURCE_NOT_FOUND" } ] } ``` ### The app's rate limit · HTTP 429 ```json { "code": 8, "message": "tool execution failed - rate limited", "details": [ { "@type": "type.googleapis.com/scalekit.v1.errdetails.ErrorInfo", "error_code": "TOOL_ERROR", "tool_error_info": { "tool_error_code": "RATE_LIMITED", "tool_error_message": "{\"error\":\"ratelimited\"}", "execution_id": "6f1c2e4a-a0b3-11f1-9c2a-0242ac120002" } } ] } ``` ### Tell them apart ```bash curl -sS ... | jq -r '.details[0] | if .error_code == "TOOL_ERROR" then "app: \(.tool_error_info.tool_error_code)" else "scalekit: \(.error_code)" end' ``` ### Handle errors Python: ```python from scalekit.common.exceptions import ( ScalekitServerException, ScalekitToolException, ScalekitToolUnauthorizedException, ) try: result = actions.execute_tool( tool_name="gmail_fetch_mails", connection_name="gmail", identifier="user_123", tool_input={"query": "is:unread"}, ) except ScalekitToolUnauthorizedException: # Access was revoked, or a stored credential was rejected: the user must authorize again link = actions.get_authorization_link( connection_name="gmail", identifier="user_123", ).link except ScalekitToolException as e: # The app rejected the call print(e.tool_error_code, e.tool_error_message, e.execution_id) except ScalekitServerException as e: # Scalekit rejected the request print(e.http_status, e.error_code, e.message) ``` Node.js: ```ts import { ScalekitServerException, ScalekitToolUnauthorizedException, isToolException, } from '@scalekit-sdk/node'; try { const result = await actions.executeTool({ toolName: 'gmail_fetch_mails', connector: 'gmail', identifier: 'user_123', toolInput: { query: 'is:unread' }, }); } catch (e) { if (e instanceof ScalekitToolUnauthorizedException) { // Access was revoked, or a stored credential was rejected: the user must authorize again const { link } = await actions.getAuthorizationLink({ connectionName: 'gmail', identifier: 'user_123', }); } else if (isToolException(e)) { // The app rejected the call console.log(e.toolErrorCode, e.toolErrorMessage, e.executionId); } else if (e instanceof ScalekitServerException) { // Scalekit rejected the request console.log(e.httpStatus, e.errorCode, e.message); } else { throw e; } } ``` The SDKs raise a tool exception for `TOOL_ERROR`: `ScalekitToolUnauthorizedException` for 401, `ScalekitToolForbiddenException` for 403, `ScalekitToolRateLimitException` for 429, and `ScalekitToolException` for the rest. Each carries `tool_error_code` and `execution_id` (`toolErrorCode` and `executionId` in Node.js). Next: [Pagination](https://docs.scalekit.com/agentkit/reference/pagination.md) --- Source: https://docs.scalekit.com/agentkit/reference/pagination.md # Pagination List endpoints return results one page at a time. Send `page_size` for how many you want. Each response carries `next_page_token`: pass it as `page_token` to get the next page, and stop when it comes back empty. Responses also carry `prev_page_token`, to page back, and `total_size`, the count across all pages. ## Endpoints that page | Endpoint | Max page_size | | --- | --- | | [List connections](https://docs.scalekit.com/agentkit/reference/connections/list-connections/) | 100 (default 10) | | [List connected accounts](https://docs.scalekit.com/agentkit/reference/connected-accounts/list-connected-accounts/) | 99 | | [Search connected accounts](https://docs.scalekit.com/agentkit/reference/connected-accounts/search-connected-accounts/) | 30 | | [List tools](https://docs.scalekit.com/agentkit/reference/tools/list-tools/) | 1000 (default 300) | | [List a user's scoped tools](https://docs.scalekit.com/agentkit/reference/tools/list-scoped-tools/) | No set limit | | [List tools available to a user](https://docs.scalekit.com/agentkit/reference/tools/list-available-tools/) | No set limit | | [List Virtual MCP servers](https://docs.scalekit.com/agentkit/reference/virtual-mcp-servers/list-virtual-mcp-servers/) | 30 | | [List accounts eligible for a trigger](https://docs.scalekit.com/agentkit/reference/triggers/list-eligible-accounts/) | 100 (default 10) | | [List trigger subscriptions](https://docs.scalekit.com/agentkit/reference/triggers/list-trigger-subscriptions/) | 100 (default 10) | | [List deliveries](https://docs.scalekit.com/agentkit/reference/triggers/list-deliveries/) | 100 (default 20) | | [List recent delivery attempts](https://docs.scalekit.com/agentkit/reference/triggers/list-recent-delivery-attempts/) | 100 (default 20) | ## Good to know - Page tokens are opaque. Pass them back as they are; don't build or parse them. - Send the same filters with every page, such as `connector` or `identifier`. - The Node.js list methods take `pageSize` and `pageToken` and return `nextPageToken`. In Python, `actions.list_tools` and `tools.list_scoped_tools` take `page_size` and `page_token`; to page through connected accounts from Python, call the REST endpoint with `page_token`. ## Examples ### Read every page REST: ```bash PAGE_TOKEN="" while :; do RES=$(curl -sS -G "$SCALEKIT_ENVIRONMENT_URL/api/v1/connected_accounts" \ -H "Authorization: Bearer $TOKEN" \ --data-urlencode "page_size=50" \ --data-urlencode "page_token=$PAGE_TOKEN") echo "$RES" | jq -c '.connected_accounts[]' PAGE_TOKEN=$(echo "$RES" | jq -r '.next_page_token // empty') [ -z "$PAGE_TOKEN" ] && break done ``` Python: ```python page_token = None while True: # summary=True fills tool_names; without it, read page.tools page = actions.list_tools(summary=True, page_size=50, page_token=page_token) for name in page.tool_names: print(name) page_token = page.next_page_token if not page_token: break ``` Node.js: ```ts let pageToken: string | undefined; do { const page = await actions.listConnectedAccounts({ pageSize: 50, pageToken }); for (const account of page.connectedAccounts) console.log(account.id); pageToken = page.nextPageToken || undefined; } while (pageToken); ``` ### Response ```json { "connected_accounts": [ ... ], "next_page_token": "...", "prev_page_token": "", "total_size": 240 } ``` --- Source: https://docs.scalekit.com/agentkit/reference/connections.md # Connections A connection is your environment's settings for one connector, such as Gmail: its name and the OAuth client ID, client secret and scopes Scalekit uses to sign users in to the app. Users then link their own accounts to it as connected accounts. See [Connections](https://docs.scalekit.com/agentkit/connections/) for setting one up in the dashboard. **Typical flow.** [Create a connection](https://docs.scalekit.com/agentkit/reference/connections/create-a-connection/) for a connector, register an OAuth app with the provider using the `redirect_uri` in the response, then [update the connection](https://docs.scalekit.com/agentkit/reference/connections/update-a-connection/) with the app's client ID, client secret and the scopes you need. One page per endpoint, each with its own markdown copy: | Endpoint | Request | What it does | Python SDK | Node.js SDK | | --- | --- | --- | --- | --- | | [Create a connection](https://docs.scalekit.com/agentkit/reference/connections/create-a-connection.md) | `POST /api/v1/agentkit/connection` | Create a connection for a connector, such as Gmail. | `actions.create_connection` | `actions.createConnection` | | [Get a connection](https://docs.scalekit.com/agentkit/reference/connections/get-a-connection.md) | `GET /api/v1/agentkit/connection/{connection_identifier}` | Get a connection and its settings, by the connection's name or ID. | `actions.get_connection` | `actions.getConnection` | | [List connections](https://docs.scalekit.com/agentkit/reference/connections/list-connections.md) | `GET /api/v1/agentkit/connection` | List connections, filtered by connector or name. | `actions.list_app_connections` | `actions.listAppConnections` | | [Update a connection](https://docs.scalekit.com/agentkit/reference/connections/update-a-connection.md) | `PATCH /api/v1/agentkit/connection/{connection_id}` | Rename a connection, or change its client ID, client secret and scopes. | `actions.update_connection` | `actions.updateConnection` | | [Delete a connection](https://docs.scalekit.com/agentkit/reference/connections/delete-a-connection.md) | `DELETE /api/v1/connections/{connection_id}` | Delete a connection. | `actions.delete_connection` | `actions.deleteConnection` | ## Create a connection `POST /api/v1/agentkit/connection` Creates a connection for a connector, such as Gmail. The connection's type comes from the connector, and an OAuth connection starts with the connector's endpoints and default scopes. Next, register an OAuth app with the provider using the `redirect_uri` from the response, then add its client ID, client secret and the scopes you need with [Update a connection](https://docs.scalekit.com/agentkit/reference/connections/update-a-connection/). **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `provider` | string | Yes | The connector's identifier, such as `GMAIL`. To find a connector's identifier, call [List connectors](https://docs.scalekit.com/agentkit/bring-your-own-connector/create-connector/#list-connectors) and read `identifier`. | | `auth_mode` | string (enum) | No | `USER` (default): each user authorizes their own account. `ORG_WIDE`: one admin authorizes an account that every user shares. You can't change it after the connection is created. One of: `USER`, `ORG_WIDE`. | | `connection_name` | string | No | A name for the connection, such as `gmail-work`: letters, numbers and hyphens, unique in the environment. Your code passes this name as `connection_name` when it creates connected accounts and runs tools. Scalekit generates one if you leave it out. | | `provider_configurations` | object | No | Extra settings, only for the connectors named on each field, such as `provider_domain` for a connector with regional domains. | | `provider_configurations.googleads_developer_token` | string | No | Google Ads only: your Google Ads API developer token. See Google's [Obtain a developer token](https://developers.google.com/google-ads/api/docs/get-started/dev-token). Responses show it masked. | | `provider_configurations.microsoft_tenant_id` | string | No | Microsoft connectors only: the ID of your Microsoft Entra tenant, from the **Overview** page of Microsoft Entra ID in the Azure portal. | | `provider_configurations.provider_domain` | string | No | The regional domain that hosts your account, such as `mcp-eu.mixpanel.com` for Mixpanel's EU region, for connectors that need one. Set it when you create the connection; it can't be changed later and isn't returned. | | `provider_configurations.slack_connection_type` | string (enum) | No | Slack only: `BOT` to act as your Slack app's bot user, or `USER` to act as the user who connects. One of: `BOT`, `USER`. | | `provider_configurations.smart_fhir_aud` | string | No | SMART on FHIR only: the base URL of the FHIR server, sent as the `aud` parameter when users sign in. | | `provider_configurations.smart_fhir_domain` | string | No | SMART on FHIR only: the domain of the FHIR server. | | `provider_configurations.trello_app_name` | string | No | Trello only: the name of your Trello app, which Trello shows users when they authorize it. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `connection` | object | The connection that was created. | | `connection.auth_mode` | string (enum) | `USER`: each user authorizes their own account. `ORG_WIDE`: one admin-authorized account is shared. One of: `USER`, `ORG_WIDE`. | | `connection.connection_mcp_url` | string | The URL of an MCP server that Scalekit hosts for this connection alone. Empty when the connection has none. | | `connection.connection_name` | string | The connection's name, such as `gmail-work`. Your code passes this name as `connection_name` when it creates connected accounts and runs tools. | | `connection.create_time` | string | When the connection was created. | | `connection.google_dwd_config` | object | Google domain-wide delegation settings. Present only for `GOOGLE_DWD` connections. | | `connection.google_dwd_config.required_scopes` | array of string | Scopes from `scopes` that users can't deselect when `scope_selection_allowed` is `true`. Each must also be in `scopes`. | | `connection.google_dwd_config.scope_selection_allowed` | boolean | Set to `true` to let users choose which of the `scopes` to grant when they connect. | | `connection.google_dwd_config.scopes` | array of string | The Google scopes to request, such as `https://www.googleapis.com/auth/gmail.readonly`. A list you send replaces the current one. Authorize the same scopes for the service account's client ID in the Google Workspace Admin console, under **Security** > **Access and data control** > **API controls** > **Manage domain wide delegation**. | | `connection.google_dwd_config.service_account_json` | string | The contents of the service account's JSON key file, as a string. Create the key in the Google Cloud console under **IAM & Admin** > **Service accounts** > your service account > **Keys** > **Add key**. Responses show it masked. | | `connection.google_dwd_config.token_uri` | string | Google's token URL. Leave it out to use `https://oauth2.googleapis.com/token`. | | `connection.id` | string | The connection's ID. | | `connection.oauth_config` | object | OAuth settings. Present for connections that use OAuth. | | `connection.oauth_config.access_type` | string | The `access_type` value sent to the provider, such as `offline`. | | `connection.oauth_config.authorize_uri` | string | The provider's authorization URL. | | `connection.oauth_config.client_id` | string | The client ID of the OAuth app you registered with the provider, such as `123456789012-abc123def456.apps.googleusercontent.com` for a Google app. Get it from the provider's developer console when you create the app (for Gmail: Google Cloud console, **APIs & Services** > **Credentials**, an OAuth client ID of type web application), and add this connection's `redirect_uri` to the app as an authorized redirect URI. Each connector's page under [Connectors](https://docs.scalekit.com/agentkit/connectors/) has the steps for that provider. | | `connection.oauth_config.client_secret` | string | The client secret of the same OAuth app, from the provider's developer console. Write-only: responses show it masked, such as `********abcd`. Leave it out to keep the stored secret. | | `connection.oauth_config.extra_authorize_params` | object | Extra query parameters to add to the provider's authorization URL, as name-value pairs, such as `{"hd": "example.com"}`, which asks Google to suggest accounts from that domain. Check the provider's OAuth documentation for the parameters it accepts. Send `{}` to clear them. | | `connection.oauth_config.optional_scopes` | object | A second set of scopes, for providers that take them in a separate authorization parameter, such as Slack's `user_scope`. Set `field_name` to that parameter's name and `scopes` to its values. | | `connection.oauth_config.pkce_enabled` | boolean | Whether Scalekit adds PKCE to the authorization request. Turn it on when the provider requires or supports PKCE. | | `connection.oauth_config.prompt` | string | The `prompt` value sent to the provider, which controls whether the user signs in or consents again, such as `consent`. Send an empty string to send no `prompt`. | | `connection.oauth_config.redirect_uri` | string | The redirect URI to add to the app you registered with the provider, as an authorized redirect URI. Scalekit sets it; you can't change it. | | `connection.oauth_config.required_optional_scopes` | array of string | Scopes from `optional_scopes.scopes` that users can't deselect when `scope_selection_allowed` is `true`. Each must also be in `optional_scopes.scopes`. | | `connection.oauth_config.required_scopes` | array of string | Scopes from `scopes` that users can't deselect when `scope_selection_allowed` is `true`, such as the read scope your tools always need. Each must also be in `scopes`, or the request fails with `400`. | | `connection.oauth_config.scope_selection_allowed` | boolean | Set to `true` to let users choose which of the `scopes` to grant when they connect. Scopes in `required_scopes` stay selected. | | `connection.oauth_config.scopes` | array of string | The OAuth scopes users grant when they connect, such as `https://www.googleapis.com/auth/gmail.readonly` for Gmail. A list you send replaces the whole current list, so include the scopes you want to keep. To build it, start from the current scopes in [Get a connection](https://docs.scalekit.com/agentkit/reference/connections/get-a-connection/), then pick from the scopes the connector offers: call [List connectors](https://docs.scalekit.com/agentkit/bring-your-own-connector/create-connector/#list-connectors) with `identifier` set to the connector, such as `GMAIL`, and read `auth_patterns[].oauth_config.available_scopes[].scope`. You can also add a scope that isn't listed there, using the exact scope string from the provider's own documentation, such as Google's [Gmail API scopes](https://developers.google.com/workspace/gmail/api/auth/scopes). | | `connection.oauth_config.token_access_type` | string | The `token_access_type` value sent to the provider, for providers that use it, such as Dropbox with `offline`. | | `connection.oauth_config.token_endpoint_auth_method` | string (enum) | How Scalekit sends client credentials to the provider's token URL. `DEFAULT` lets Scalekit choose. One of: `NONE`, `BASIC_AUTH`, `REQUEST_BODY`, `DEFAULT`. | | `connection.oauth_config.token_uri` | string | The provider's token URL. | | `connection.oauth_config.use_scalekit_creds` | boolean | Set to `true` to use Scalekit's own OAuth app instead of yours, for connectors that offer one; otherwise the request fails with `400`. Sending your own `client_id` or `client_secret` sets it to `false` unless you also send it. | | `connection.oauth_config.user_info_uri` | string | The provider's user info URL. | | `connection.provider` | string | The connector this connection uses, such as `GMAIL`. | | `connection.provider_configurations` | object | Settings for connectors that need extra ones, such as Slack. | | `connection.provider_configurations.googleads_developer_token` | string | Google Ads only: your Google Ads API developer token. See Google's [Obtain a developer token](https://developers.google.com/google-ads/api/docs/get-started/dev-token). Responses show it masked. | | `connection.provider_configurations.microsoft_tenant_id` | string | Microsoft connectors only: the ID of your Microsoft Entra tenant, from the **Overview** page of Microsoft Entra ID in the Azure portal. | | `connection.provider_configurations.provider_domain` | string | The regional domain that hosts your account, such as `mcp-eu.mixpanel.com` for Mixpanel's EU region, for connectors that need one. Set it when you create the connection; it can't be changed later and isn't returned. | | `connection.provider_configurations.slack_connection_type` | string (enum) | Slack only: `BOT` to act as your Slack app's bot user, or `USER` to act as the user who connects. One of: `BOT`, `USER`. | | `connection.provider_configurations.smart_fhir_aud` | string | SMART on FHIR only: the base URL of the FHIR server, sent as the `aud` parameter when users sign in. | | `connection.provider_configurations.smart_fhir_domain` | string | SMART on FHIR only: the domain of the FHIR server. | | `connection.provider_configurations.trello_app_name` | string | Trello only: the name of your Trello app, which Trello shows users when they authorize it. | | `connection.type` | string (enum) | How users authenticate to the app, such as `OAUTH`. It comes from the connector and decides which settings object applies (`oauth_config` or `google_dwd_config`). One of: `OIDC`, `SAML`, `PASSWORD`, `OAUTH`, `PASSWORDLESS`, `BASIC`, `BEARER`, `API_KEY`, `WEBAUTHN`, `OAUTH_M2M`, `TRELLO_OAUTH1`, `GOOGLE_DWD`, `TRUSTED_IDP`, `SMART_FHIR`, `NO_AUTH`. | | `connection.update_time` | string | When the connection was last changed. | **Errors** - `400`: Invalid request - the connector is missing or unknown, or the connection name is already used or reserved. Error code `DUPLICATE_IDENTIFIER` when the name is already used. - `401`: Authentication required - missing or invalid access token **Request** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/agentkit/connection" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "provider": "GMAIL", "connection_name": "gmail-work" }' ``` **Response** ```json { "connection": { "id": "conn_70219645518265104", "type": "OAUTH", "create_time": "2026-10-02T14:30:00Z", "update_time": "2026-10-02T14:30:00Z", "provider": "GMAIL", "connection_name": "gmail-work", "auth_mode": "USER", "oauth_config": { "authorize_uri": "https://accounts.google.com/o/oauth2/v2/auth", "token_uri": "https://oauth2.googleapis.com/token", "user_info_uri": "https://www.googleapis.com/oauth2/v2/userinfo", "client_id": "", "client_secret": "", "scopes": [ "https://www.googleapis.com/auth/gmail.readonly", "https://www.googleapis.com/auth/gmail.modify", "https://www.googleapis.com/auth/gmail.send", "https://www.googleapis.com/auth/contacts.readonly", "https://www.googleapis.com/auth/contacts.other.readonly", "https://www.googleapis.com/auth/gmail.settings.basic" ], "redirect_uri": "https://your-env.scalekit.dev/sso/v1/oauth/conn_70219645518265104/callback", "pkce_enabled": false, "use_scalekit_creds": false, "extra_authorize_params": {}, "token_endpoint_auth_method": "DEFAULT", "scope_selection_allowed": false, "required_scopes": [], "required_optional_scopes": [] } } } ``` **Python SDK:** `scalekit_client.actions.create_connection` Creates a connection for a connector, such as `GMAIL`. Then set its credentials and scopes with `update_connection`. ```python scalekit_client.actions.create_connection( provider: str, connection_name: Optional[str] = None, auth_mode: Optional[AuthMode] = None, provider_domain: Optional[str] = None, ) -> AgentKitConnection ``` Example: ```python result = scalekit_client.actions.create_connection( provider="GMAIL", connection_name="gmail-work", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `provider` | `str` | Yes | The connector's identifier, such as `GMAIL`. To find it, call [List connectors](https://docs.scalekit.com/agentkit/bring-your-own-connector/create-connector/#list-connectors) and read `identifier`. | | `connection_name` | `Optional[str]` | No | A name for the connection, such as `gmail-work`: letters, numbers and hyphens, unique in the environment. Your code passes this name when it creates connected accounts and runs tools. Scalekit generates one if you leave it out. | | `auth_mode` | `Optional[AuthMode]` | No | `AuthMode.USER` (default): each user authorizes their own account. `AuthMode.ORG_WIDE`: one admin authorizes an account that every user shares. Import it with `from scalekit.actions.connections import AuthMode`. You can't change it later. | | `provider_domain` | `Optional[str]` | No | The regional domain, for connectors that need one, such as `mcp-eu.mixpanel.com` for Mixpanel's EU region. You can't change it later. | Returns `AgentKitConnection`: The created connection. **Node.js SDK:** `scalekit.actions.createConnection` Creates a connection for a connector, such as `GMAIL`. Then set its credentials and scopes with `updateConnection`. ```ts scalekit.actions.createConnection( params: { provider: string; connectionName?: string; authMode?: AgentKitAuthMode; providerDomain?: string; }, ): Promise ``` Example: ```ts const result = await scalekit.actions.createConnection({ provider: "GMAIL", connectionName: "gmail-work", }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `provider` | `string` | Yes | The connector's identifier, such as `GMAIL`. To find it, call [List connectors](https://docs.scalekit.com/agentkit/bring-your-own-connector/create-connector/#list-connectors) and read `identifier`. | | `connectionName` | `string` | No | A name for the connection, such as `gmail-work`: letters, numbers and hyphens, unique in the environment. Your code passes this name when it creates connected accounts and runs tools. Scalekit generates one if you leave it out. | | `authMode` | `AgentKitAuthMode` | No | `'USER'` (default): each user authorizes their own account. `'ORG_WIDE'`: one admin authorizes an account that every user shares. You can't change it later. | | `providerDomain` | `string` | No | The regional domain, for connectors that need one, such as `mcp-eu.mixpanel.com` for Mixpanel's EU region. You can't change it later. | Returns `Promise`: The created connection. **Used in** - [Set up a connection](https://docs.scalekit.com/agentkit/connections/) ## Get a connection `GET /api/v1/agentkit/connection/{connection_identifier}` Returns a connection with its settings, looked up by its name or its ID. Use it to check the scopes the connection requests, or to copy its `redirect_uri` into the app you registered with the provider. Secrets come back masked. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connection_identifier` | string | Yes | The connection's name, such as `gmail-work`, or its ID, such as `conn_70219645518265104`. Both are in the [List connections](https://docs.scalekit.com/agentkit/reference/connections/list-connections/) response. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `connection` | object | The connection. | | `connection.auth_mode` | string (enum) | `USER`: each user authorizes their own account. `ORG_WIDE`: one admin-authorized account is shared. One of: `USER`, `ORG_WIDE`. | | `connection.connection_mcp_url` | string | The URL of an MCP server that Scalekit hosts for this connection alone. Empty when the connection has none. | | `connection.connection_name` | string | The connection's name, such as `gmail-work`. Your code passes this name as `connection_name` when it creates connected accounts and runs tools. | | `connection.create_time` | string | When the connection was created. | | `connection.google_dwd_config` | object | Google domain-wide delegation settings. Present only for `GOOGLE_DWD` connections. | | `connection.google_dwd_config.required_scopes` | array of string | Scopes from `scopes` that users can't deselect when `scope_selection_allowed` is `true`. Each must also be in `scopes`. | | `connection.google_dwd_config.scope_selection_allowed` | boolean | Set to `true` to let users choose which of the `scopes` to grant when they connect. | | `connection.google_dwd_config.scopes` | array of string | The Google scopes to request, such as `https://www.googleapis.com/auth/gmail.readonly`. A list you send replaces the current one. Authorize the same scopes for the service account's client ID in the Google Workspace Admin console, under **Security** > **Access and data control** > **API controls** > **Manage domain wide delegation**. | | `connection.google_dwd_config.service_account_json` | string | The contents of the service account's JSON key file, as a string. Create the key in the Google Cloud console under **IAM & Admin** > **Service accounts** > your service account > **Keys** > **Add key**. Responses show it masked. | | `connection.google_dwd_config.token_uri` | string | Google's token URL. Leave it out to use `https://oauth2.googleapis.com/token`. | | `connection.id` | string | The connection's ID. | | `connection.oauth_config` | object | OAuth settings. Present for connections that use OAuth. | | `connection.oauth_config.access_type` | string | The `access_type` value sent to the provider, such as `offline`. | | `connection.oauth_config.authorize_uri` | string | The provider's authorization URL. | | `connection.oauth_config.client_id` | string | The client ID of the OAuth app you registered with the provider, such as `123456789012-abc123def456.apps.googleusercontent.com` for a Google app. Get it from the provider's developer console when you create the app (for Gmail: Google Cloud console, **APIs & Services** > **Credentials**, an OAuth client ID of type web application), and add this connection's `redirect_uri` to the app as an authorized redirect URI. Each connector's page under [Connectors](https://docs.scalekit.com/agentkit/connectors/) has the steps for that provider. | | `connection.oauth_config.client_secret` | string | The client secret of the same OAuth app, from the provider's developer console. Write-only: responses show it masked, such as `********abcd`. Leave it out to keep the stored secret. | | `connection.oauth_config.extra_authorize_params` | object | Extra query parameters to add to the provider's authorization URL, as name-value pairs, such as `{"hd": "example.com"}`, which asks Google to suggest accounts from that domain. Check the provider's OAuth documentation for the parameters it accepts. Send `{}` to clear them. | | `connection.oauth_config.optional_scopes` | object | A second set of scopes, for providers that take them in a separate authorization parameter, such as Slack's `user_scope`. Set `field_name` to that parameter's name and `scopes` to its values. | | `connection.oauth_config.pkce_enabled` | boolean | Whether Scalekit adds PKCE to the authorization request. Turn it on when the provider requires or supports PKCE. | | `connection.oauth_config.prompt` | string | The `prompt` value sent to the provider, which controls whether the user signs in or consents again, such as `consent`. Send an empty string to send no `prompt`. | | `connection.oauth_config.redirect_uri` | string | The redirect URI to add to the app you registered with the provider, as an authorized redirect URI. Scalekit sets it; you can't change it. | | `connection.oauth_config.required_optional_scopes` | array of string | Scopes from `optional_scopes.scopes` that users can't deselect when `scope_selection_allowed` is `true`. Each must also be in `optional_scopes.scopes`. | | `connection.oauth_config.required_scopes` | array of string | Scopes from `scopes` that users can't deselect when `scope_selection_allowed` is `true`, such as the read scope your tools always need. Each must also be in `scopes`, or the request fails with `400`. | | `connection.oauth_config.scope_selection_allowed` | boolean | Set to `true` to let users choose which of the `scopes` to grant when they connect. Scopes in `required_scopes` stay selected. | | `connection.oauth_config.scopes` | array of string | The OAuth scopes users grant when they connect, such as `https://www.googleapis.com/auth/gmail.readonly` for Gmail. A list you send replaces the whole current list, so include the scopes you want to keep. To build it, start from the current scopes in [Get a connection](https://docs.scalekit.com/agentkit/reference/connections/get-a-connection/), then pick from the scopes the connector offers: call [List connectors](https://docs.scalekit.com/agentkit/bring-your-own-connector/create-connector/#list-connectors) with `identifier` set to the connector, such as `GMAIL`, and read `auth_patterns[].oauth_config.available_scopes[].scope`. You can also add a scope that isn't listed there, using the exact scope string from the provider's own documentation, such as Google's [Gmail API scopes](https://developers.google.com/workspace/gmail/api/auth/scopes). | | `connection.oauth_config.token_access_type` | string | The `token_access_type` value sent to the provider, for providers that use it, such as Dropbox with `offline`. | | `connection.oauth_config.token_endpoint_auth_method` | string (enum) | How Scalekit sends client credentials to the provider's token URL. `DEFAULT` lets Scalekit choose. One of: `NONE`, `BASIC_AUTH`, `REQUEST_BODY`, `DEFAULT`. | | `connection.oauth_config.token_uri` | string | The provider's token URL. | | `connection.oauth_config.use_scalekit_creds` | boolean | Set to `true` to use Scalekit's own OAuth app instead of yours, for connectors that offer one; otherwise the request fails with `400`. Sending your own `client_id` or `client_secret` sets it to `false` unless you also send it. | | `connection.oauth_config.user_info_uri` | string | The provider's user info URL. | | `connection.provider` | string | The connector this connection uses, such as `GMAIL`. | | `connection.provider_configurations` | object | Settings for connectors that need extra ones, such as Slack. | | `connection.provider_configurations.googleads_developer_token` | string | Google Ads only: your Google Ads API developer token. See Google's [Obtain a developer token](https://developers.google.com/google-ads/api/docs/get-started/dev-token). Responses show it masked. | | `connection.provider_configurations.microsoft_tenant_id` | string | Microsoft connectors only: the ID of your Microsoft Entra tenant, from the **Overview** page of Microsoft Entra ID in the Azure portal. | | `connection.provider_configurations.provider_domain` | string | The regional domain that hosts your account, such as `mcp-eu.mixpanel.com` for Mixpanel's EU region, for connectors that need one. Set it when you create the connection; it can't be changed later and isn't returned. | | `connection.provider_configurations.slack_connection_type` | string (enum) | Slack only: `BOT` to act as your Slack app's bot user, or `USER` to act as the user who connects. One of: `BOT`, `USER`. | | `connection.provider_configurations.smart_fhir_aud` | string | SMART on FHIR only: the base URL of the FHIR server, sent as the `aud` parameter when users sign in. | | `connection.provider_configurations.smart_fhir_domain` | string | SMART on FHIR only: the domain of the FHIR server. | | `connection.provider_configurations.trello_app_name` | string | Trello only: the name of your Trello app, which Trello shows users when they authorize it. | | `connection.type` | string (enum) | How users authenticate to the app, such as `OAUTH`. It comes from the connector and decides which settings object applies (`oauth_config` or `google_dwd_config`). One of: `OIDC`, `SAML`, `PASSWORD`, `OAUTH`, `PASSWORDLESS`, `BASIC`, `BEARER`, `API_KEY`, `WEBAUTHN`, `OAUTH_M2M`, `TRELLO_OAUTH1`, `GOOGLE_DWD`, `TRUSTED_IDP`, `SMART_FHIR`, `NO_AUTH`. | | `connection.update_time` | string | When the connection was last changed. | **Errors** - `401`: Authentication required - missing or invalid access token - `404`: Not found - no connection with this name or ID. Error code `RESOURCE_NOT_FOUND`. **Request** ```bash curl -sS -X GET \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/agentkit/connection/gmail-work" \ -H "Authorization: Bearer $TOKEN" ``` **Response** ```json { "connection": { "id": "conn_70219645518265104", "type": "OAUTH", "create_time": "2026-10-02T14:30:00Z", "update_time": "2026-10-02T14:30:00Z", "provider": "GMAIL", "connection_name": "gmail-work", "auth_mode": "USER", "oauth_config": { "authorize_uri": "https://accounts.google.com/o/oauth2/v2/auth", "token_uri": "https://oauth2.googleapis.com/token", "user_info_uri": "https://www.googleapis.com/oauth2/v2/userinfo", "client_id": "", "client_secret": "", "scopes": [ "https://www.googleapis.com/auth/gmail.readonly", "https://www.googleapis.com/auth/gmail.modify", "https://www.googleapis.com/auth/gmail.send", "https://www.googleapis.com/auth/contacts.readonly", "https://www.googleapis.com/auth/contacts.other.readonly", "https://www.googleapis.com/auth/gmail.settings.basic" ], "redirect_uri": "https://your-env.scalekit.dev/sso/v1/oauth/conn_70219645518265104/callback", "pkce_enabled": false, "use_scalekit_creds": false, "extra_authorize_params": {}, "token_endpoint_auth_method": "DEFAULT", "scope_selection_allowed": false, "required_scopes": [], "required_optional_scopes": [] }, "connection_mcp_url": "https://your-env.scalekit.dev/mcp/v3/connections/gmail-work" } } ``` **Python SDK:** `scalekit_client.actions.get_connection` Gets a connection by name or ID. Pass the connection's name, or its ID with `connection_id`. ```python scalekit_client.actions.get_connection( connection_name: Optional[str] = None, connection_id: Optional[str] = None, ) -> AgentKitConnection ``` Example: ```python result = scalekit_client.actions.get_connection( connection_name="gmail-work", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_name` | `Optional[str]` | No | The connection's name, such as `gmail-work`. | | `connection_id` | `Optional[str]` | No | The connection's ID, such as `conn_70219645518265104`, instead of its name. Both are in the [List connections](https://docs.scalekit.com/agentkit/reference/connections/list-connections/) response. | Returns `AgentKitConnection`. **Node.js SDK:** `scalekit.actions.getConnection` Gets a connection by name or ID. Pass the connection's name or ID. ```ts scalekit.actions.getConnection( connection: string | ConnectionRef, ): Promise ``` Example: ```ts const result = await scalekit.actions.getConnection("gmail-work"); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection` | `string \| ConnectionRef` | Yes | The connection's name, such as `gmail-work`, or its ID, such as `conn_70219645518265104`, as a string, or `{ connectionName }` or `{ connectionId }`. Both are in the [List connections](https://docs.scalekit.com/agentkit/reference/connections/list-connections/) response. | Returns `Promise`. **Used in** - [Set up a connection](https://docs.scalekit.com/agentkit/connections/) ## List connections `GET /api/v1/agentkit/connection` Lists the connections in the environment, newest first, without their settings. Filter by connector, or search by name. Use it to find a connection's name or ID, or to check whether a connector already has a connection; call [Get a connection](https://docs.scalekit.com/agentkit/reference/connections/get-a-connection/) for one connection's settings. **Query parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `page_size` | integer | No | The number of connections to return, up to 100. Defaults to 10. | | `page_token` | string | No | The `next_page_token` from the previous response, to get the next page. Leave it out for the first page. | | `provider` | string | No | Return only connections for this connector, by its identifier, such as `GMAIL`. A connection's `provider` field holds the same value. | | `query` | string | No | Return connections whose name, connector or connector display name contains this text, such as `gmail`. At least 3 characters. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `connections` | array of object | The connections on this page, newest first. | | `connections.auth_mode` | string (enum) | `USER`: each user authorizes their own account. `ORG_WIDE`: one admin-authorized account is shared. One of: `USER`, `ORG_WIDE`. | | `connections.connection_name` | string | The connection's name. | | `connections.create_time` | string | When the connection was created. | | `connections.id` | string | The connection's ID. | | `connections.provider` | string | The connector this connection uses, such as `GMAIL`. | | `connections.type` | string (enum) | How users authenticate to the app, such as `OAUTH`. One of: `OIDC`, `SAML`, `PASSWORD`, `OAUTH`, `PASSWORDLESS`, `BASIC`, `BEARER`, `API_KEY`, `WEBAUTHN`, `OAUTH_M2M`, `TRELLO_OAUTH1`, `GOOGLE_DWD`, `TRUSTED_IDP`, `SMART_FHIR`, `NO_AUTH`. | | `next_page_token` | string | Pass as `page_token` to get the next page. Empty on the last page. | | `prev_page_token` | string | Pass as `page_token` to get the previous page. | | `total_size` | integer | The number of connections that match. | **Errors** - `400`: Invalid request - for example, `query` is shorter than 3 characters. - `401`: Authentication required - missing or invalid access token **Request** ```bash curl -sS -G -X GET \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/agentkit/connection" \ -H "Authorization: Bearer $TOKEN" \ --data-urlencode "provider=GMAIL" \ --data-urlencode "page_size=10" ``` **Response** ```json { "connections": [ { "id": "conn_70219645518265104", "connection_name": "gmail-work", "provider": "GMAIL", "type": "OAUTH", "auth_mode": "USER", "create_time": "2026-10-02T14:30:00Z" }, { "id": "conn_70219645518265377", "connection_name": "gmail-support", "provider": "GMAIL", "type": "OAUTH", "auth_mode": "USER", "create_time": "2026-09-28T09:12:00Z" } ], "next_page_token": "", "prev_page_token": "", "total_size": 2 } ``` **Python SDK:** `scalekit_client.actions.list_app_connections` Lists connections, newest first. Filter by connector with `provider`, or search by name with `query`. ```python scalekit_client.actions.list_app_connections( provider: Optional[str] = None, query: Optional[str] = None, page_size: Optional[int] = None, page_token: Optional[str] = None, ) -> AgentKitConnectionList ``` Example: ```python result = scalekit_client.actions.list_app_connections( provider="GMAIL", page_size=10, ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `provider` | `Optional[str]` | No | Return only connections for this connector, by its identifier, such as `GMAIL`. A connection's `provider` field holds the same value. | | `query` | `Optional[str]` | No | Return connections whose name, connector or connector display name contains this text, such as `gmail`. At least 3 characters. | | `page_size` | `Optional[int]` | No | The number of connections to return, up to 100. Defaults to 10. | | `page_token` | `Optional[str]` | No | The `next_page_token` from the previous response, to get the next page. Leave it out for the first page. | Returns `AgentKitConnectionList`. **Node.js SDK:** `scalekit.actions.listAppConnections` Lists connections, newest first. Filter by connector with `provider`, or search by name with `query`. ```ts scalekit.actions.listAppConnections( params?: { provider?: string; query?: string; pageSize?: number; pageToken?: string; }, ): Promise ``` Example: ```ts const result = await scalekit.actions.listAppConnections({ provider: "GMAIL", pageSize: 10, }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `provider` | `string` | No | Return only connections for this connector, by its identifier, such as `GMAIL`. A connection's `provider` field holds the same value. | | `query` | `string` | No | Return connections whose name, connector or connector display name contains this text, such as `gmail`. At least 3 characters. | | `pageSize` | `number` | No | The number of connections to return, up to 100. Defaults to 10. | | `pageToken` | `string` | No | The `nextPageToken` from the previous response, to get the next page. Leave it out for the first page. | Returns `Promise`. ## Update a connection `PATCH /api/v1/agentkit/connection/{connection_id}` Changes a connection's name or settings. Send only the fields you want to change; everything else keeps its current value, including the stored client secret. To clear a list or a map, send it empty (`[]` or `{}`). To see the current settings first, call [Get a connection](https://docs.scalekit.com/agentkit/reference/connections/get-a-connection/). Sending your own `client_id` or `client_secret` switches the connection to your credentials, unless you also set `use_scalekit_creds`. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | string | Yes | The connection's ID, such as `conn_70219645518265104`. Use the ID, not the name. Find it in the `id` field of the [Create](https://docs.scalekit.com/agentkit/reference/connections/create-a-connection/), [Get](https://docs.scalekit.com/agentkit/reference/connections/get-a-connection/) or [List connections](https://docs.scalekit.com/agentkit/reference/connections/list-connections/) response. | **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connection_name` | string | No | A new name for the connection, such as `gmail-prod`: letters, numbers and hyphens, unique in the environment. Your code refers to the connection by this name, so update the `connection_name` it passes when you rename one. | | `google_dwd_config` | object | No | Google domain-wide delegation settings to change. Only for connections whose `type` is `GOOGLE_DWD`. | | `google_dwd_config.required_scopes` | array of string | No | Scopes from `scopes` that users can't deselect when `scope_selection_allowed` is `true`. Each must also be in `scopes`. | | `google_dwd_config.scope_selection_allowed` | boolean | No | Set to `true` to let users choose which of the `scopes` to grant when they connect. | | `google_dwd_config.scopes` | array of string | No | The Google scopes to request, such as `https://www.googleapis.com/auth/gmail.readonly`. A list you send replaces the current one. Authorize the same scopes for the service account's client ID in the Google Workspace Admin console, under **Security** > **Access and data control** > **API controls** > **Manage domain wide delegation**. | | `google_dwd_config.service_account_json` | string | No | The contents of the service account's JSON key file, as a string. Create the key in the Google Cloud console under **IAM & Admin** > **Service accounts** > your service account > **Keys** > **Add key**. Responses show it masked. | | `google_dwd_config.token_uri` | string | No | Google's token URL. Leave it out to use `https://oauth2.googleapis.com/token`. | | `oauth_config` | object | No | OAuth settings to change, such as `client_id`, `client_secret` and `scopes`. For connections that use OAuth, such as `type` `OAUTH`. | | `oauth_config.access_type` | string | No | The `access_type` value sent to the provider, such as `offline`. | | `oauth_config.authorize_uri` | string | No | The provider's authorization URL. | | `oauth_config.client_id` | string | No | The client ID of the OAuth app you registered with the provider, such as `123456789012-abc123def456.apps.googleusercontent.com` for a Google app. Get it from the provider's developer console when you create the app (for Gmail: Google Cloud console, **APIs & Services** > **Credentials**, an OAuth client ID of type web application), and add this connection's `redirect_uri` to the app as an authorized redirect URI. Each connector's page under [Connectors](https://docs.scalekit.com/agentkit/connectors/) has the steps for that provider. | | `oauth_config.client_secret` | string | No | The client secret of the same OAuth app, from the provider's developer console. Write-only: responses show it masked, such as `********abcd`. Leave it out to keep the stored secret. | | `oauth_config.extra_authorize_params` | object | No | Extra query parameters to add to the provider's authorization URL, as name-value pairs, such as `{"hd": "example.com"}`, which asks Google to suggest accounts from that domain. Check the provider's OAuth documentation for the parameters it accepts. Send `{}` to clear them. | | `oauth_config.optional_scopes` | object | No | A second set of scopes, for providers that take them in a separate authorization parameter, such as Slack's `user_scope`. Set `field_name` to that parameter's name and `scopes` to its values. | | `oauth_config.optional_scopes.field_name` | string | No | Name of the field in which scope should be sent in the authentication request. This is required by some identity providers that expect scopes to be sent in a custom field instead of the standard 'scope' parameter. | | `oauth_config.optional_scopes.scopes` | array of string | No | List of optional scopes that can be requested during authentication. | | `oauth_config.pkce_enabled` | boolean | No | Whether Scalekit adds PKCE to the authorization request. Turn it on when the provider requires or supports PKCE. | | `oauth_config.prompt` | string | No | The `prompt` value sent to the provider, which controls whether the user signs in or consents again, such as `consent`. Send an empty string to send no `prompt`. | | `oauth_config.redirect_uri` | string | No | The redirect URI to add to the app you registered with the provider, as an authorized redirect URI. Scalekit sets it; you can't change it. | | `oauth_config.required_optional_scopes` | array of string | No | Scopes from `optional_scopes.scopes` that users can't deselect when `scope_selection_allowed` is `true`. Each must also be in `optional_scopes.scopes`. | | `oauth_config.required_scopes` | array of string | No | Scopes from `scopes` that users can't deselect when `scope_selection_allowed` is `true`, such as the read scope your tools always need. Each must also be in `scopes`, or the request fails with `400`. | | `oauth_config.scope_selection_allowed` | boolean | No | Set to `true` to let users choose which of the `scopes` to grant when they connect. Scopes in `required_scopes` stay selected. | | `oauth_config.scopes` | array of string | No | The OAuth scopes users grant when they connect, such as `https://www.googleapis.com/auth/gmail.readonly` for Gmail. A list you send replaces the whole current list, so include the scopes you want to keep. To build it, start from the current scopes in [Get a connection](https://docs.scalekit.com/agentkit/reference/connections/get-a-connection/), then pick from the scopes the connector offers: call [List connectors](https://docs.scalekit.com/agentkit/bring-your-own-connector/create-connector/#list-connectors) with `identifier` set to the connector, such as `GMAIL`, and read `auth_patterns[].oauth_config.available_scopes[].scope`. You can also add a scope that isn't listed there, using the exact scope string from the provider's own documentation, such as Google's [Gmail API scopes](https://developers.google.com/workspace/gmail/api/auth/scopes). | | `oauth_config.token_access_type` | string | No | The `token_access_type` value sent to the provider, for providers that use it, such as Dropbox with `offline`. | | `oauth_config.token_endpoint_auth_method` | string (enum) | No | How Scalekit sends client credentials to the provider's token URL. `DEFAULT` lets Scalekit choose. One of: `NONE`, `BASIC_AUTH`, `REQUEST_BODY`, `DEFAULT`. | | `oauth_config.token_uri` | string | No | The provider's token URL. | | `oauth_config.use_scalekit_creds` | boolean | No | Set to `true` to use Scalekit's own OAuth app instead of yours, for connectors that offer one; otherwise the request fails with `400`. Sending your own `client_id` or `client_secret` sets it to `false` unless you also send it. | | `oauth_config.user_info_uri` | string | No | The provider's user info URL. | | `provider_configurations` | object | No | Connector-specific settings to change, for the connectors listed on each field. `provider_domain` can only be set when you create the connection. | | `provider_configurations.googleads_developer_token` | string | No | Google Ads only: your Google Ads API developer token. See Google's [Obtain a developer token](https://developers.google.com/google-ads/api/docs/get-started/dev-token). Responses show it masked. | | `provider_configurations.microsoft_tenant_id` | string | No | Microsoft connectors only: the ID of your Microsoft Entra tenant, from the **Overview** page of Microsoft Entra ID in the Azure portal. | | `provider_configurations.provider_domain` | string | No | The regional domain that hosts your account, such as `mcp-eu.mixpanel.com` for Mixpanel's EU region, for connectors that need one. Set it when you create the connection; it can't be changed later and isn't returned. | | `provider_configurations.slack_connection_type` | string (enum) | No | Slack only: `BOT` to act as your Slack app's bot user, or `USER` to act as the user who connects. One of: `BOT`, `USER`. | | `provider_configurations.smart_fhir_aud` | string | No | SMART on FHIR only: the base URL of the FHIR server, sent as the `aud` parameter when users sign in. | | `provider_configurations.smart_fhir_domain` | string | No | SMART on FHIR only: the domain of the FHIR server. | | `provider_configurations.trello_app_name` | string | No | Trello only: the name of your Trello app, which Trello shows users when they authorize it. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `connection` | object | The connection with its new settings. | | `connection.auth_mode` | string (enum) | `USER`: each user authorizes their own account. `ORG_WIDE`: one admin-authorized account is shared. One of: `USER`, `ORG_WIDE`. | | `connection.connection_mcp_url` | string | The URL of an MCP server that Scalekit hosts for this connection alone. Empty when the connection has none. | | `connection.connection_name` | string | The connection's name, such as `gmail-work`. Your code passes this name as `connection_name` when it creates connected accounts and runs tools. | | `connection.create_time` | string | When the connection was created. | | `connection.google_dwd_config` | object | Google domain-wide delegation settings. Present only for `GOOGLE_DWD` connections. | | `connection.google_dwd_config.required_scopes` | array of string | Scopes from `scopes` that users can't deselect when `scope_selection_allowed` is `true`. Each must also be in `scopes`. | | `connection.google_dwd_config.scope_selection_allowed` | boolean | Set to `true` to let users choose which of the `scopes` to grant when they connect. | | `connection.google_dwd_config.scopes` | array of string | The Google scopes to request, such as `https://www.googleapis.com/auth/gmail.readonly`. A list you send replaces the current one. Authorize the same scopes for the service account's client ID in the Google Workspace Admin console, under **Security** > **Access and data control** > **API controls** > **Manage domain wide delegation**. | | `connection.google_dwd_config.service_account_json` | string | The contents of the service account's JSON key file, as a string. Create the key in the Google Cloud console under **IAM & Admin** > **Service accounts** > your service account > **Keys** > **Add key**. Responses show it masked. | | `connection.google_dwd_config.token_uri` | string | Google's token URL. Leave it out to use `https://oauth2.googleapis.com/token`. | | `connection.id` | string | The connection's ID. | | `connection.oauth_config` | object | OAuth settings. Present for connections that use OAuth. | | `connection.oauth_config.access_type` | string | The `access_type` value sent to the provider, such as `offline`. | | `connection.oauth_config.authorize_uri` | string | The provider's authorization URL. | | `connection.oauth_config.client_id` | string | The client ID of the OAuth app you registered with the provider, such as `123456789012-abc123def456.apps.googleusercontent.com` for a Google app. Get it from the provider's developer console when you create the app (for Gmail: Google Cloud console, **APIs & Services** > **Credentials**, an OAuth client ID of type web application), and add this connection's `redirect_uri` to the app as an authorized redirect URI. Each connector's page under [Connectors](https://docs.scalekit.com/agentkit/connectors/) has the steps for that provider. | | `connection.oauth_config.client_secret` | string | The client secret of the same OAuth app, from the provider's developer console. Write-only: responses show it masked, such as `********abcd`. Leave it out to keep the stored secret. | | `connection.oauth_config.extra_authorize_params` | object | Extra query parameters to add to the provider's authorization URL, as name-value pairs, such as `{"hd": "example.com"}`, which asks Google to suggest accounts from that domain. Check the provider's OAuth documentation for the parameters it accepts. Send `{}` to clear them. | | `connection.oauth_config.optional_scopes` | object | A second set of scopes, for providers that take them in a separate authorization parameter, such as Slack's `user_scope`. Set `field_name` to that parameter's name and `scopes` to its values. | | `connection.oauth_config.pkce_enabled` | boolean | Whether Scalekit adds PKCE to the authorization request. Turn it on when the provider requires or supports PKCE. | | `connection.oauth_config.prompt` | string | The `prompt` value sent to the provider, which controls whether the user signs in or consents again, such as `consent`. Send an empty string to send no `prompt`. | | `connection.oauth_config.redirect_uri` | string | The redirect URI to add to the app you registered with the provider, as an authorized redirect URI. Scalekit sets it; you can't change it. | | `connection.oauth_config.required_optional_scopes` | array of string | Scopes from `optional_scopes.scopes` that users can't deselect when `scope_selection_allowed` is `true`. Each must also be in `optional_scopes.scopes`. | | `connection.oauth_config.required_scopes` | array of string | Scopes from `scopes` that users can't deselect when `scope_selection_allowed` is `true`, such as the read scope your tools always need. Each must also be in `scopes`, or the request fails with `400`. | | `connection.oauth_config.scope_selection_allowed` | boolean | Set to `true` to let users choose which of the `scopes` to grant when they connect. Scopes in `required_scopes` stay selected. | | `connection.oauth_config.scopes` | array of string | The OAuth scopes users grant when they connect, such as `https://www.googleapis.com/auth/gmail.readonly` for Gmail. A list you send replaces the whole current list, so include the scopes you want to keep. To build it, start from the current scopes in [Get a connection](https://docs.scalekit.com/agentkit/reference/connections/get-a-connection/), then pick from the scopes the connector offers: call [List connectors](https://docs.scalekit.com/agentkit/bring-your-own-connector/create-connector/#list-connectors) with `identifier` set to the connector, such as `GMAIL`, and read `auth_patterns[].oauth_config.available_scopes[].scope`. You can also add a scope that isn't listed there, using the exact scope string from the provider's own documentation, such as Google's [Gmail API scopes](https://developers.google.com/workspace/gmail/api/auth/scopes). | | `connection.oauth_config.token_access_type` | string | The `token_access_type` value sent to the provider, for providers that use it, such as Dropbox with `offline`. | | `connection.oauth_config.token_endpoint_auth_method` | string (enum) | How Scalekit sends client credentials to the provider's token URL. `DEFAULT` lets Scalekit choose. One of: `NONE`, `BASIC_AUTH`, `REQUEST_BODY`, `DEFAULT`. | | `connection.oauth_config.token_uri` | string | The provider's token URL. | | `connection.oauth_config.use_scalekit_creds` | boolean | Set to `true` to use Scalekit's own OAuth app instead of yours, for connectors that offer one; otherwise the request fails with `400`. Sending your own `client_id` or `client_secret` sets it to `false` unless you also send it. | | `connection.oauth_config.user_info_uri` | string | The provider's user info URL. | | `connection.provider` | string | The connector this connection uses, such as `GMAIL`. | | `connection.provider_configurations` | object | Settings for connectors that need extra ones, such as Slack. | | `connection.provider_configurations.googleads_developer_token` | string | Google Ads only: your Google Ads API developer token. See Google's [Obtain a developer token](https://developers.google.com/google-ads/api/docs/get-started/dev-token). Responses show it masked. | | `connection.provider_configurations.microsoft_tenant_id` | string | Microsoft connectors only: the ID of your Microsoft Entra tenant, from the **Overview** page of Microsoft Entra ID in the Azure portal. | | `connection.provider_configurations.provider_domain` | string | The regional domain that hosts your account, such as `mcp-eu.mixpanel.com` for Mixpanel's EU region, for connectors that need one. Set it when you create the connection; it can't be changed later and isn't returned. | | `connection.provider_configurations.slack_connection_type` | string (enum) | Slack only: `BOT` to act as your Slack app's bot user, or `USER` to act as the user who connects. One of: `BOT`, `USER`. | | `connection.provider_configurations.smart_fhir_aud` | string | SMART on FHIR only: the base URL of the FHIR server, sent as the `aud` parameter when users sign in. | | `connection.provider_configurations.smart_fhir_domain` | string | SMART on FHIR only: the domain of the FHIR server. | | `connection.provider_configurations.trello_app_name` | string | Trello only: the name of your Trello app, which Trello shows users when they authorize it. | | `connection.type` | string (enum) | How users authenticate to the app, such as `OAUTH`. It comes from the connector and decides which settings object applies (`oauth_config` or `google_dwd_config`). One of: `OIDC`, `SAML`, `PASSWORD`, `OAUTH`, `PASSWORDLESS`, `BASIC`, `BEARER`, `API_KEY`, `WEBAUTHN`, `OAUTH_M2M`, `TRELLO_OAUTH1`, `GOOGLE_DWD`, `TRUSTED_IDP`, `SMART_FHIR`, `NO_AUTH`. | | `connection.update_time` | string | When the connection was last changed. | **Errors** - `400`: Invalid request - for example, an unknown field, a name that is already used, settings that don't match the connection's type, `required_scopes` that aren't all in `scopes`, or `use_scalekit_creds` on a connector that doesn't offer Scalekit credentials. - `401`: Authentication required - missing or invalid access token - `404`: Not found - no connection with this ID. Error code `RESOURCE_NOT_FOUND`. **Request** ```bash curl -sS -X PATCH \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/agentkit/connection/conn_70219645518265104" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "connection_name": "gmail-prod", "oauth_config": { "client_id": "[CLIENT ID]", "client_secret": "[CLIENT SECRET]", "scopes": [ "https://www.googleapis.com/auth/gmail.readonly" ] } }' ``` **Response** ```json { "connection": { "id": "conn_70219645518265104", "type": "OAUTH", "create_time": "2026-10-02T14:30:00Z", "update_time": "2026-10-02T15:10:00Z", "provider": "GMAIL", "connection_name": "gmail-prod", "auth_mode": "USER", "oauth_config": { "authorize_uri": "https://accounts.google.com/o/oauth2/v2/auth", "token_uri": "https://oauth2.googleapis.com/token", "user_info_uri": "https://www.googleapis.com/oauth2/v2/userinfo", "client_id": "[CLIENT ID]", "client_secret": "********abcd", "scopes": [ "https://www.googleapis.com/auth/gmail.readonly" ], "redirect_uri": "https://your-env.scalekit.dev/sso/v1/oauth/conn_70219645518265104/callback", "pkce_enabled": false, "use_scalekit_creds": false, "extra_authorize_params": {}, "token_endpoint_auth_method": "DEFAULT", "scope_selection_allowed": false, "required_scopes": [], "required_optional_scopes": [] } } } ``` **Python SDK:** `scalekit_client.actions.update_connection` Changes a connection's name or settings. Pass the connection by name, or by ID with `connection_id`. Only the fields you pass change. ```python scalekit_client.actions.update_connection( connection_name: Optional[str] = None, connection_id: Optional[str] = None, new_connection_name: Optional[str] = None, oauth: Optional[OAuthSettings] = None, google_dwd: Optional[GoogleDWDSettings] = None, provider_configurations: Optional[ProviderConfigurations] = None, ) -> AgentKitConnection ``` Example: ```python from scalekit.actions.connections import OAuthSettings result = scalekit_client.actions.update_connection( connection_id="conn_70219645518265104", new_connection_name="gmail-prod", oauth=OAuthSettings( client_id="[CLIENT ID]", client_secret="[CLIENT SECRET]", scopes=["https://www.googleapis.com/auth/gmail.readonly"], ), ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_name` | `Optional[str]` | No | The connection's current name, such as `gmail-work`. Use this or `connection_id`. | | `connection_id` | `Optional[str]` | No | The connection's ID, such as `conn_70219645518265104`. Use this or `connection_name`. Both are in the [List connections](https://docs.scalekit.com/agentkit/reference/connections/list-connections/) response. | | `new_connection_name` | `Optional[str]` | No | A new name for the connection, such as `gmail-prod`. Update the `connection_name` your code passes when you rename one. | | `oauth` | `Optional[OAuthSettings]` | No | The OAuth settings to change, as `OAuthSettings`. Each field you leave as `None` keeps its value; pass `[]` or `{}` to clear a list or map. `client_id` and `client_secret` come from the OAuth app you registered with the provider, such as a Google Cloud OAuth client ID for Gmail; add the connection's `redirect_uri` to that app. `scopes` replaces the whole list, such as `["https://www.googleapis.com/auth/gmail.readonly"]`. Start from the current scopes in [Get a connection](https://docs.scalekit.com/agentkit/reference/connections/get-a-connection/), and pick more from `auth_patterns[].oauth_config.available_scopes` in [List connectors](https://docs.scalekit.com/agentkit/bring-your-own-connector/create-connector/#list-connectors). You can also add a scope that isn't listed there, using the exact string from the provider's documentation, such as Google's [Gmail API scopes](https://developers.google.com/workspace/gmail/api/auth/scopes). Other fields: `required_scopes`, `scope_selection_allowed`, `use_scalekit_creds`, `access_type`, `prompt`, `pkce_enabled`, `extra_authorize_params` and `token_endpoint_auth_method`, described in the request body above. | | `google_dwd` | `Optional[GoogleDWDSettings]` | No | Google domain-wide delegation settings to change, as `GoogleDWDSettings(service_account_json=, scopes=)`. Only for `GOOGLE_DWD` connections. | | `provider_configurations` | `Optional[ProviderConfigurations]` | No | Connector-specific settings to change, as `ProviderConfigurations`, such as `ProviderConfigurations(slack_connection_type="BOT")` for Slack. | Returns `AgentKitConnection`. **Node.js SDK:** `scalekit.actions.updateConnection` Changes a connection's name or settings. Pass the connection by `connectionName` or `connectionId`. Only the fields you pass change. ```ts scalekit.actions.updateConnection( params: { connectionName?: string; connectionId?: string; newConnectionName?: string; oauth?: AgentKitOAuthSettings; googleDwd?: AgentKitGoogleDwdSettings; providerConfigurations?: AgentKitProviderConfigurations; }, ): Promise ``` Example: ```ts const result = await scalekit.actions.updateConnection({ connectionId: "conn_70219645518265104", newConnectionName: "gmail-prod", oauth: { clientId: "[CLIENT ID]", clientSecret: "[CLIENT SECRET]", scopes: ["https://www.googleapis.com/auth/gmail.readonly"], }, }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connectionName` | `string` | No | The connection's current name, such as `gmail-work`. Use this or `connectionId`. | | `connectionId` | `string` | No | The connection's ID, such as `conn_70219645518265104`. Use this or `connectionName`. Both are in the [List connections](https://docs.scalekit.com/agentkit/reference/connections/list-connections/) response. | | `newConnectionName` | `string` | No | A new name for the connection, such as `gmail-prod`. Update the `connectionName` your code passes when you rename one. | | `oauth` | `AgentKitOAuthSettings` | No | The OAuth settings to change, as `{ clientId?, clientSecret?, scopes?, ... }`. Each field you leave out keeps its value; pass `[]` or `{}` to clear a list or map. `clientId` and `clientSecret` come from the OAuth app you registered with the provider, such as a Google Cloud OAuth client ID for Gmail; add the connection's `redirect_uri` to that app. `scopes` replaces the whole list, such as `["https://www.googleapis.com/auth/gmail.readonly"]`. Start from the current scopes in [Get a connection](https://docs.scalekit.com/agentkit/reference/connections/get-a-connection/), and pick more from `auth_patterns[].oauth_config.available_scopes` in [List connectors](https://docs.scalekit.com/agentkit/bring-your-own-connector/create-connector/#list-connectors). You can also add a scope that isn't listed there, using the exact string from the provider's documentation, such as Google's [Gmail API scopes](https://developers.google.com/workspace/gmail/api/auth/scopes). Other fields: `requiredScopes`, `scopeSelectionAllowed`, `useScalekitCreds`, `accessType`, `prompt`, `pkceEnabled`, `extraAuthorizeParams` and `tokenEndpointAuthMethod`, described in the request body above in snake_case. Send at most one of `oauth` or `googleDwd`. | | `googleDwd` | `AgentKitGoogleDwdSettings` | No | Google domain-wide delegation settings to change, as `{ serviceAccountJson?, scopes?, ... }`. Only for `GOOGLE_DWD` connections. | | `providerConfigurations` | `AgentKitProviderConfigurations` | No | Connector-specific settings to change, such as `{ slackConnectionType: 'BOT' }` for Slack. Send it with `oauth`, or on its own. | Returns `Promise`. **Used in** - [Set up a connection](https://docs.scalekit.com/agentkit/connections/) ## Delete a connection `DELETE /api/v1/connections/{connection_id}` Deletes a connection. It fails while connected accounts or Virtual MCP servers still use the connection. Delete its connected accounts first with [Delete a connected account](https://docs.scalekit.com/agentkit/reference/connected-accounts/delete-a-connected-account/), and remove it from each server with [Update a Virtual MCP server](https://docs.scalekit.com/agentkit/reference/virtual-mcp-servers/update-a-virtual-mcp-server/). **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | string | Yes | The connection's ID, such as `conn_70219645518265104`. Find it in the `id` field of the [List connections](https://docs.scalekit.com/agentkit/reference/connections/list-connections/) response. | **Errors** - `400`: Invalid request - connected accounts or Virtual MCP servers still use the connection. - `401`: Authentication required - missing or invalid access token - `404`: Not found - no connection with this ID. Error code `RESOURCE_NOT_FOUND`. **Request** ```bash curl -sS -X DELETE \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connections/conn_70219645518265104" \ -H "Authorization: Bearer $TOKEN" ``` **Response** ```json {} ``` **Python SDK:** `scalekit_client.actions.delete_connection` Deletes a connection. Pass the connection's name, or its ID with `connection_id`. It fails while connected accounts or Virtual MCP servers still use the connection, so delete those accounts and remove the connection from those servers first. ```python scalekit_client.actions.delete_connection( connection_name: Optional[str] = None, connection_id: Optional[str] = None, ) -> None ``` Example: ```python result = scalekit_client.actions.delete_connection( connection_id="conn_70219645518265104", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_name` | `Optional[str]` | No | The connection's name, such as `gmail-work`. | | `connection_id` | `Optional[str]` | No | The connection's ID, such as `conn_70219645518265104`, instead of its name. Both are in the [List connections](https://docs.scalekit.com/agentkit/reference/connections/list-connections/) response. | Returns `None`: Nothing. The call raises an error if it fails. **Node.js SDK:** `scalekit.actions.deleteConnection` Deletes a connection. Pass the connection's name or ID. It fails while connected accounts or Virtual MCP servers still use the connection, so delete those accounts and remove the connection from those servers first. ```ts scalekit.actions.deleteConnection( connection: string | ConnectionRef, ): Promise ``` Example: ```ts const result = await scalekit.actions.deleteConnection({ connectionId: "conn_70219645518265104" }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection` | `string \| ConnectionRef` | Yes | The connection's name, such as `gmail-work`, or its ID, such as `conn_70219645518265104`, as a string, or `{ connectionName }` or `{ connectionId }`. | Returns `Promise`: Nothing. The promise rejects if the call fails. --- Source: https://docs.scalekit.com/agentkit/reference/connected-accounts.md # Connected accounts A connected account is one user's link to one connection, and holds that user's credentials for the app. See [Manage connected accounts](https://docs.scalekit.com/agentkit/connected-accounts/) for when to check, refresh or delete one. **SDK helpers.** `get_or_create_connected_account` (Python) and `getOrCreateConnectedAccount` (Node.js) get the user's connected account for a connection, or create it without credentials when there isn't one; [Authorize a user](https://docs.scalekit.com/agentkit/tools/authorize/) and [Manage connected accounts](https://docs.scalekit.com/agentkit/connected-accounts/) use them. `actions.providers.list_providers` (Python) and `actions.providers.listProviders` (Node.js) list connectors with their `identifier`, as [Create a connector](https://docs.scalekit.com/agentkit/bring-your-own-connector/create-connector/) shows. One page per endpoint, each with its own markdown copy: | Endpoint | Request | What it does | Python SDK | Node.js SDK | | --- | --- | --- | --- | --- | | [Create a connected account](https://docs.scalekit.com/agentkit/reference/connected-accounts/create-a-connected-account.md) | `POST /api/v1/connected_accounts` | Store a user's OAuth tokens or API key for one connection. | `actions.create_connected_account` | `actions.createConnectedAccount` | | [Get a connected account](https://docs.scalekit.com/agentkit/reference/connected-accounts/get-a-connected-account.md) | `GET /api/v1/connected_accounts/details` | Get an account's status, connection and settings, without its credentials. | `actions.get_connected_account_details` | REST only | | [List connected accounts](https://docs.scalekit.com/agentkit/reference/connected-accounts/list-connected-accounts.md) | `GET /api/v1/connected_accounts` | List accounts, filtered by connection, user, provider or organization. | `actions.list_connected_accounts` | `actions.listConnectedAccounts` | | [Search connected accounts](https://docs.scalekit.com/agentkit/reference/connected-accounts/search-connected-accounts.md) | `GET /api/v1/connected_accounts:search` | Find accounts whose identifier, provider or connection matches a text query. | REST only | REST only | | [Update a connected account](https://docs.scalekit.com/agentkit/reference/connected-accounts/update-a-connected-account.md) | `PUT /api/v1/connected_accounts` | Store new tokens or an API key for an account, or change its settings. | `actions.update_connected_account` | `actions.updateConnectedAccount` | | [Get a connected account's credentials](https://docs.scalekit.com/agentkit/reference/connected-accounts/get-connected-account-credentials.md) | `GET /api/v1/connected_accounts/auth` | Get an account with its OAuth tokens or API key, to call the app yourself. | `actions.get_connected_account` | `actions.getConnectedAccount` | | [Delete a connected account](https://docs.scalekit.com/agentkit/reference/connected-accounts/delete-a-connected-account.md) | `POST /api/v1/connected_accounts:delete` | Delete an account and the user's stored credentials. | `actions.delete_connected_account` | `actions.deleteConnectedAccount` | ## Create a connected account `POST /api/v1/connected_accounts` Creates a connected account for one user and one connection, with credentials your app already holds: OAuth tokens, or an API key or other static credentials. To have the user connect their own account instead, send them an authorization link. Returns the account with its ID and status. To store a static credential, send it as `authorization_details.static_auth.details` with the key that matches the connection's authentication type: `token` for bearer, `api_key` for API key, or `username` and `password` for basic auth. Check `status` in the response: an `ACTIVE` account is ready for tool calls. If it is `PENDING_AUTH`, send the same details with [Update a connected account](https://docs.scalekit.com/agentkit/reference/connected-accounts/update-a-connected-account/) to activate it. **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connected_account` | object | Yes | Details of the connected account to create. | | `connected_account.api_config` | object | No | Optional JSON configuration for connector-specific API settings such as rate limits, custom API endpoints, timeouts, or feature flags. | | `connected_account.authorization_details` | object | No | Authentication credentials for the connected account. Include OAuth tokens (access_token, refresh_token, scopes) or static auth details (API keys, bearer tokens). Can be provided later via update. | | `connected_account.authorization_details.google_dwd` | object | No | Google Domain-Wide Delegation authentication — used for GOOGLE_DWD connections. Send only subject in requests; access_token, scopes, and token_expires_at are response-only. | | `connected_account.authorization_details.oauth_token` | object | No | OAuth 2.0 credentials. | | `connected_account.authorization_details.static_auth` | object | No | Static credentials, such as an API key. Send them in `details`. The keys depend on the connection's authentication type: `token` for bearer, `api_key` for API key, and `username` and `password` for basic auth. Providers that need several API key headers take one key per header. | | `connected_account.authorization_details.static_auth.details` | object | Yes | The credential, as a JSON object. Send the key that matches the connection's authentication type: `token` for bearer, `api_key` for API key, or `username` and `password` for basic auth. Add `domain` or `version` here when the provider needs them. | | `connected_account.authorization_details.static_auth.details.api_key` | string | No | For an **API key** connection: the key. Required. Providers that need several headers take one key per header instead, named by the provider's field name or header name. | | `connected_account.authorization_details.static_auth.details.password` | string | No | For a **basic auth** connection: the password. Leave it empty for providers that take a key as the username. | | `connected_account.authorization_details.static_auth.details.token` | string | No | For a **bearer** connection: the access token. Required. Scalekit sends it to the provider for you. | | `connected_account.authorization_details.static_auth.details.username` | string | No | For a **basic auth** connection: the username. Required. | | `connected_account.authorization_details.trusted_idp` | object | No | Credentials for a connection that signs in through a trusted identity provider, such as AWS Redshift. Send only `db_user`. Responses include `access_key_id` and `expiry`, never the secret key or session token. | | `connector` | string | Yes | The connection name, as shown in **AgentKit** > **Connections**. | | `identifier` | string | No | Your app's ID for the user. Use a stable internal ID, not an email address. Required unless you key the account by `organization_id`. | | `organization_id` | string | No | An organization ID to key the account by instead of `identifier`, such as a Scalekit organization ID. Ignored when `identifier` is set. | | `user_id` | string | No | A user ID that, with `organization_id`, keys the account to one user in that organization. Ignored when `identifier` is set. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `connected_account` | object | The newly created connected account with its unique identifier, status, and complete authorization details including access tokens. | | `connected_account.api_config` | object | Optional JSON configuration for connector-specific API settings such as rate limits, custom endpoints, or feature flags. | | `connected_account.authorization_details` | object | The account's credentials. Set the one that matches the connection's auth type - `oauth_token`, `static_auth`, `google_dwd` or `trusted_idp`. | | `connected_account.authorization_details.google_dwd` | object | Google Domain-Wide Delegation authentication — used for GOOGLE_DWD connections. Send only subject in requests; access_token, scopes, and token_expires_at are response-only. | | `connected_account.authorization_details.oauth_token` | object | OAuth 2.0 credentials. | | `connected_account.authorization_details.static_auth` | object | Static credentials, such as an API key. Send them in `details`. The keys depend on the connection's authentication type: `token` for bearer, `api_key` for API key, and `username` and `password` for basic auth. Providers that need several API key headers take one key per header. | | `connected_account.authorization_details.trusted_idp` | object | Credentials for a connection that signs in through a trusted identity provider, such as AWS Redshift. Send only `db_user`. Responses include `access_key_id` and `expiry`, never the secret key or session token. | | `connected_account.authorization_type` | string (enum) | Type of authorization mechanism used. Specifies whether this connection uses OAuth, API keys, bearer tokens, or other auth methods. One of: `OAUTH`, `API_KEY`, `BASIC_AUTH`, `BEARER_TOKEN`, `CUSTOM`, `BASIC`, `OAUTH_M2M`, `TRELLO_OAUTH1`, `GOOGLE_DWD`, `TRUSTED_IDP`, `SMART_FHIR`, `NO_AUTH`. | | `connected_account.connection_id` | string | Reference to the parent connection configuration. Links this account to a specific connector setup in your environment. | | `connected_account.connector` | string | The connection name, as shown in **AgentKit** > **Connections**. | | `connected_account.id` | string | Unique Scalekit-generated identifier for this connected account. Always prefixed with 'ca_'. | | `connected_account.identifier` | string | Your app's ID for the user, the value passed when the account was created. | | `connected_account.is_org_wide_credential` | boolean | Whether this is the shared credential of an org-wide connection, which every user's tool calls on that connection use. `false` for a user's own account. | | `connected_account.last_used_at` | string | Timestamp when this connected account was last used to make an API call. Useful for tracking active connections. | | `connected_account.provider` | string | The app the account connects to, such as `GMAIL` or `SLACK`. | | `connected_account.status` | string (enum) | Current status of the connected account. Indicates if the account is active, expired, pending authorization, or pending user identity verification. One of: `ACTIVE`, `EXPIRED`, `PENDING_AUTH`, `PENDING_VERIFICATION`, `DISCONNECTED`. | | `connected_account.token_expires_at` | string | Expiration timestamp for the access token. After this time, the token must be refreshed or re-authorized. | | `connected_account.updated_at` | string | Timestamp when this connected account was last modified. Updated whenever credentials or configuration changes. | **Errors** - `400`: Invalid request - missing required fields, invalid authorization details, or validation failed. Error code `RESOURCE_ALREADY_EXISTS` when the user already has a connected account on this connection; update it instead. - `401`: Authentication required - missing or invalid access token - `404`: Not found - no connection with this name exists in the environment. Error code `RESOURCE_NOT_FOUND`. **Request** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connected_accounts" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "connector": "gmail", "identifier": "user_123", "connected_account": { "authorization_details": { "oauth_token": { "access_token": "[ACCESS TOKEN]", "refresh_token": "[REFRESH TOKEN]", "scopes": [ "https://www.googleapis.com/auth/gmail.readonly" ] } } } }' ``` **Request with a bearer token** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connected_accounts" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "connector": "bearer-connection", "identifier": "user_123", "connected_account": { "authorization_details": { "static_auth": { "details": { "token": "[BEARER TOKEN]" } } } } }' ``` ```python result = scalekit_client.actions.create_connected_account( connection_name="bearer-connection", identifier="user_123", authorization_details={ "static_auth": { "token": "[BEARER TOKEN]", }, }, ) ``` ```ts const result = await scalekit.actions.createConnectedAccount({ connectionName: "bearer-connection", identifier: "user_123", authorizationDetails: { details: { case: "staticAuth", value: { details: { token: "[BEARER TOKEN]", }, }, }, }, }); ``` **Request with an API key** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connected_accounts" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "connector": "api-key-connection", "identifier": "user_123", "connected_account": { "authorization_details": { "static_auth": { "details": { "api_key": "[API KEY]" } } } } }' ``` ```python result = scalekit_client.actions.create_connected_account( connection_name="api-key-connection", identifier="user_123", authorization_details={ "static_auth": { "api_key": "[API KEY]", }, }, ) ``` ```ts const result = await scalekit.actions.createConnectedAccount({ connectionName: "api-key-connection", identifier: "user_123", authorizationDetails: { details: { case: "staticAuth", value: { details: { api_key: "[API KEY]", }, }, }, }, }); ``` **Request with basic auth** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connected_accounts" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "connector": "basic-auth-connection", "identifier": "user_123", "connected_account": { "authorization_details": { "static_auth": { "details": { "username": "[USERNAME]", "password": "[PASSWORD]" } } } } }' ``` ```python result = scalekit_client.actions.create_connected_account( connection_name="basic-auth-connection", identifier="user_123", authorization_details={ "static_auth": { "username": "[USERNAME]", "password": "[PASSWORD]", }, }, ) ``` ```ts const result = await scalekit.actions.createConnectedAccount({ connectionName: "basic-auth-connection", identifier: "user_123", authorizationDetails: { details: { case: "staticAuth", value: { details: { username: "[USERNAME]", password: "[PASSWORD]", }, }, }, }, }); ``` **Response** ```json { "connected_account": { "id": "ca_24834495392086178", "connection_id": "conn_70219645518265104", "connector": "gmail", "identifier": "user_123", "provider": "GMAIL", "authorization_type": "OAUTH", "status": "ACTIVE", "is_org_wide_credential": false, "token_expires_at": "2026-10-02T15:30:00Z", "last_used_at": "2026-10-02T14:31:07Z", "updated_at": "2026-10-02T14:30:00Z", "authorization_details": { "oauth_token": { "access_token": "[ACCESS TOKEN]", "refresh_token": "[REFRESH TOKEN]", "scopes": [ "https://www.googleapis.com/auth/gmail.readonly" ] } } } } ``` **Python SDK:** `scalekit_client.actions.create_connected_account` Create a new connected account ```python scalekit_client.actions.create_connected_account( connection_name: str, identifier: str, authorization_details: Optional[Dict[str, Any]] = None, organization_id: Optional[str] = None, user_id: Optional[str] = None, api_config: Optional[Dict[str, Any]] = None, ) -> CreateConnectedAccountResponse ``` Example: ```python result = scalekit_client.actions.create_connected_account( connection_name="gmail", identifier="user_123", authorization_details={ "oauth_token": { "access_token": "[ACCESS TOKEN]", "refresh_token": "[REFRESH TOKEN]", "scopes": ["https://www.googleapis.com/auth/gmail.readonly"], }, }, ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_name` | `str` | Yes | The connection name, as shown in **AgentKit** > **Connections**. | | `identifier` | `str` | Yes | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `authorization_details` | `Optional[Dict[str, Any]]` | No | The user's credentials as a dict: `{"oauth_token": {...}}` for OAuth tokens, or `{"static_auth": {...}}` for static credentials, holding the key the connection reads: `token` for bearer, `api_key` for API key, or `username` and `password` for basic auth. Unlike the REST body, `static_auth` has no `details` level. | | `organization_id` | `Optional[str]` | No | Organization ID | | `user_id` | `Optional[str]` | No | User ID | | `api_config` | `Optional[Dict[str, Any]]` | No | Optional API configuration for the connected account | Returns `CreateConnectedAccountResponse`: The created connected account details **Node.js SDK:** `scalekit.actions.createConnectedAccount` Create a new connected account. This helper accepts a high-level payload and builds the underlying CreateConnectedAccount message. ```ts scalekit.actions.createConnectedAccount( params: { connectionName: string; identifier: string; authorizationDetails: MessageInitShape['authorizationDetails']; organizationId?: string; userId?: string; apiConfig?: Record; }, ): Promise ``` Example: ```ts const result = await scalekit.actions.createConnectedAccount({ connectionName: "gmail", identifier: "user_123", authorizationDetails: { details: { case: "oauthToken", value: { accessToken: "[ACCESS TOKEN]", refreshToken: "[REFRESH TOKEN]", scopes: ["https://www.googleapis.com/auth/gmail.readonly"], }, }, }, }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connectionName` | `string` | Yes | The connection name, as shown in **AgentKit** > **Connections**. | | `identifier` | `string` | Yes | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `authorizationDetails` | `MessageInitShape['authorizationDetails']` | Yes | The user's credentials: OAuth tokens as `{ case: "oauthToken", value }`, or static credentials as `{ case: "staticAuth", value }`, where `value.details` holds the key the connection reads: `token` for bearer, `api_key` for API key, or `username` and `password` for basic auth. To have the user connect their own account instead, use `getAuthorizationLink`. | | `organizationId` | `string` | No | An organization ID to key the account by instead of `identifier`, such as a Scalekit organization ID. Ignored when `identifier` is set. | | `userId` | `string` | No | A user ID that, with `organization_id`, keys the account to one user in that organization. Ignored when `identifier` is set. | | `apiConfig` | `Record` | No | Optional JSON configuration for connector-specific API settings such as rate limits, custom API endpoints, timeouts, or feature flags. | Returns `Promise`. **Used in** - [Apify Actor with per-user OAuth via Scalekit](https://docs.scalekit.com/cookbooks/apify-actor-per-user-oauth/) - [Authorize a user](https://docs.scalekit.com/agentkit/tools/authorize/) - [Build a daily briefing agent with Vercel AI SDK and Scalekit AgentKit](https://docs.scalekit.com/cookbooks/daily-briefing-agent/) - [Build a Mastra agent with Scalekit AgentKit tools](https://docs.scalekit.com/cookbooks/mastra-agentkit/) - [Build a multi-agent email triage crew with CrewAI](https://docs.scalekit.com/cookbooks/crewai-agentkit-email-triage/) - [Build a multi-user GitHub PR summarizer agent](https://docs.scalekit.com/cookbooks/render-github-pr-summarizer/) - [Build an agent that books meetings and drafts emails](https://docs.scalekit.com/cookbooks/schedule-meeting-and-draft-email/) - [FastRouter + Scalekit tool calling](https://docs.scalekit.com/cookbooks/fastrouter-agentkit-tool-calling/) - [Manage connected accounts](https://docs.scalekit.com/agentkit/connected-accounts/) - [Migrate from Composio to Scalekit](https://docs.scalekit.com/agentkit/advanced/migrate-from-composio/) - [Quickstart](https://docs.scalekit.com/agentkit/quickstart/) - [Trace AgentKit tool calls in LangSmith](https://docs.scalekit.com/cookbooks/langsmith-tracing-agentkit/) Also called by `scalekit_client.actions.get_or_create_connected_account` (Python), `scalekit.actions.getOrCreateConnectedAccount` (Node.js). ## Get a connected account `GET /api/v1/connected_accounts/details` Returns a connected account's status, connection and settings, looked up by its ID or by `connector` and `identifier`. It never includes the account's credentials. To call the app yourself with them, use [Get a connected account's credentials](https://docs.scalekit.com/agentkit/reference/connected-accounts/get-connected-account-credentials/). **Query parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connector` | string | No | The connection name, as shown in **AgentKit** > **Connections**. | | `id` | string | No | Unique identifier for the connected account. | | `identifier` | string | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `organization_id` | string | No | An organization ID to key the account by instead of `identifier`, such as a Scalekit organization ID. Ignored when `identifier` is set. | | `user_id` | string | No | A user ID that, with `organization_id`, keys the account to one user in that organization. Ignored when `identifier` is set. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `connected_account` | object | The connected account. | | `connected_account.api_config` | object | Optional JSON configuration for connector-specific API settings such as rate limits, custom endpoints, or feature flags. | | `connected_account.authorization_type` | string (enum) | Type of authorization mechanism used. Specifies whether this connection uses OAuth, API keys, bearer tokens, or other auth methods. One of: `OAUTH`, `API_KEY`, `BASIC_AUTH`, `BEARER_TOKEN`, `CUSTOM`, `BASIC`, `OAUTH_M2M`, `TRELLO_OAUTH1`, `GOOGLE_DWD`, `TRUSTED_IDP`, `SMART_FHIR`, `NO_AUTH`. | | `connected_account.connection_id` | string | Reference to the parent connection configuration. Links this account to a specific connector setup in your environment. | | `connected_account.connector` | string | The connection name, as shown in **AgentKit** > **Connections**. | | `connected_account.id` | string | Unique Scalekit-generated identifier for this connected account. Always prefixed with 'ca_'. | | `connected_account.identifier` | string | Your app's ID for the user, the value passed when the account was created. | | `connected_account.is_org_wide_credential` | boolean | Whether this is the shared credential of an org-wide connection, which every user's tool calls on that connection use. `false` for a user's own account. | | `connected_account.last_used_at` | string | Timestamp when this connected account was last used to make an API call. Useful for tracking active connections. | | `connected_account.provider` | string | The app the account connects to, such as `GMAIL` or `SLACK`. | | `connected_account.status` | string (enum) | Current status of the connected account. Indicates if the account is active, expired, pending authorization, or pending user identity verification. One of: `ACTIVE`, `EXPIRED`, `PENDING_AUTH`, `PENDING_VERIFICATION`, `DISCONNECTED`. | | `connected_account.token_expires_at` | string | Expiration timestamp for the access token. After this time, the token must be refreshed or re-authorized. | | `connected_account.updated_at` | string | Timestamp when this connected account was last modified. Updated whenever credentials or configuration changes. | **Errors** - `400`: Invalid request - missing required query parameters - `401`: Authentication required - missing or invalid access token - `404`: Connected account not found - no account matches the specified criteria **Request** ```bash curl -sS -G -X GET \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connected_accounts/details" \ -H "Authorization: Bearer $TOKEN" \ --data-urlencode "connector=gmail" \ --data-urlencode "identifier=user_123" ``` **Response** ```json { "connected_account": { "id": "ca_24834495392086178", "connection_id": "conn_70219645518265104", "connector": "gmail", "identifier": "user_123", "provider": "GMAIL", "authorization_type": "OAUTH", "status": "ACTIVE", "is_org_wide_credential": false, "token_expires_at": "2026-10-02T15:30:00Z", "last_used_at": "2026-10-02T14:31:07Z", "updated_at": "2026-10-02T14:30:00Z" } } ``` **Python SDK:** `scalekit_client.actions.get_connected_account_details` Gets a connected account without its credentials. Identify the account with `connection_name` and `identifier`, or with `connected_account_id`. To get its credentials, use `get_connected_account`. ```python scalekit_client.actions.get_connected_account_details( connection_name: Optional[str] = None, identifier: Optional[str] = None, connected_account_id: Optional[str] = None, ) -> GetConnectedAccountDetailsResponse ``` Example: ```python result = scalekit_client.actions.get_connected_account_details( connection_name="gmail", identifier="user_123", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_name` | `Optional[str]` | No | The connection name, as shown in **AgentKit** > **Connections**. | | `identifier` | `Optional[str]` | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `connected_account_id` | `Optional[str]` | No | Scalekit connected account ID. When supplied, `connection_name` and `identifier` are ignored. | Returns `GetConnectedAccountDetailsResponse`: The account metadata without auth credentials **Node.js:** Call this endpoint over REST, as the example shows. ```ts const envUrl = process.env.SCALEKIT_ENVIRONMENT_URL!; const tokenResponse = await fetch(`${envUrl}/oauth/token`, { method: "POST", body: new URLSearchParams({ grant_type: "client_credentials", client_id: process.env.SCALEKIT_CLIENT_ID!, client_secret: process.env.SCALEKIT_CLIENT_SECRET!, }), }); const token = (await tokenResponse.json()).access_token; const params = new URLSearchParams({ connector: "gmail", identifier: "user_123" }); const response = await fetch(`${envUrl}/api/v1/connected_accounts/details?${params}`, { method: "GET", headers: { Authorization: `Bearer ${token}`, }, }); if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`); const result = await response.json(); ``` **Used in** - [Call any API](https://docs.scalekit.com/agentkit/tools/custom-tools/) - [Quickstart](https://docs.scalekit.com/agentkit/quickstart/) - [Troubleshoot connection and OAuth errors](https://docs.scalekit.com/agentkit/troubleshooting/) ## List connected accounts `GET /api/v1/connected_accounts` Lists the connected accounts in the environment, filtered by connection, identifier, provider or organization, with each account's status. The list doesn't include credentials; use [Get a connected account's credentials](https://docs.scalekit.com/agentkit/reference/connected-accounts/get-connected-account-credentials/) for one account. Use `next_page_token` to get the next page. **Query parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connection_names` | array of string | No | Filter by one or more connection names (exact match). Returns connected accounts belonging to any of the specified connections. Max 20 names per request. Cannot be combined with the `connector` field. | | `connector` | string | No | The connection name, as shown in **AgentKit** > **Connections**. | | `identifier` | string | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `is_org_wide_credential` | boolean | No | Set to `true` to return only the shared credential of an org-wide connection, or `false` to return only users' own accounts. Omit it to return both. | | `organization_id` | string | No | Filter by organization ID. Returns only connected accounts associated with this organization. | | `page_size` | integer | No | Maximum number of connected accounts to return, up to 99. Defaults to 10 when omitted or 0. | | `page_token` | string | No | Pagination token from a previous response. Use the next_page_token value from ListConnectedAccountsResponse to fetch the next page. | | `provider` | string | No | Return only accounts for this app, such as `GMAIL` or `SLACK`. Exact match, in capitals as the account's `provider` shows it. | | `query` | string | No | Text search query to filter connected accounts by name, identifier, or other searchable fields. Case-insensitive. | | `user_id` | string | No | Filter by user ID. Returns only connected accounts associated with this user. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `connected_accounts` | array of object | List of connected accounts matching the filter criteria. Excludes sensitive authorization details for security. | | `connected_accounts.authorization_type` | string (enum) | Authorization mechanism type. One of: `OAUTH`, `API_KEY`, `BASIC_AUTH`, `BEARER_TOKEN`, `CUSTOM`, `BASIC`, `OAUTH_M2M`, `TRELLO_OAUTH1`, `GOOGLE_DWD`, `TRUSTED_IDP`, `SMART_FHIR`, `NO_AUTH`. | | `connected_accounts.connection_id` | string | Parent connection configuration reference. | | `connected_accounts.connector` | string | The connection name, as shown in **AgentKit** > **Connections**. | | `connected_accounts.id` | string | Unique connected account identifier. | | `connected_accounts.identifier` | string | Your app's ID for the user, the value passed when the account was created. | | `connected_accounts.is_org_wide_credential` | boolean | Whether this is the shared credential of an org-wide connection, which every user's tool calls on that connection use. `false` for a user's own account. | | `connected_accounts.last_used_at` | string | Last usage timestamp. | | `connected_accounts.provider` | string | The app the account connects to, such as `GMAIL` or `SLACK`. | | `connected_accounts.status` | string (enum) | Current connection status. One of: `ACTIVE`, `EXPIRED`, `PENDING_AUTH`, `PENDING_VERIFICATION`, `DISCONNECTED`. | | `connected_accounts.token_expires_at` | string | Token expiration timestamp. | | `connected_accounts.updated_at` | string | Last modification timestamp. | | `next_page_token` | string | Pagination token for retrieving the next page. Empty if this is the last page. Pass this value to page_token in the next request. | | `prev_page_token` | string | Pagination token for retrieving the previous page. Empty if this is the first page. Pass this value to page_token to go back. | | `total_size` | integer | Total count of connected accounts matching the filter criteria across all pages. Use for calculating pagination. | **Errors** - `400`: Invalid request - occurs when query parameters are malformed or validation fails - `401`: Authentication required - missing or invalid access token **Request** ```bash curl -sS -G -X GET \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connected_accounts" \ -H "Authorization: Bearer $TOKEN" \ --data-urlencode "connector=gmail" \ --data-urlencode "identifier=user_123" ``` **Response** ```json { "connected_accounts": [ { "id": "ca_24834495392086178", "connection_id": "conn_70219645518265104", "connector": "gmail", "identifier": "user_123", "provider": "GMAIL", "authorization_type": "OAUTH", "status": "ACTIVE", "is_org_wide_credential": false, "token_expires_at": "2026-10-02T15:30:00Z", "last_used_at": "2026-10-02T14:31:07Z", "updated_at": "2026-10-02T14:30:00Z" } ], "next_page_token": "", "prev_page_token": "", "total_size": 1 } ``` **Python SDK:** `scalekit_client.actions.list_connected_accounts` List connected accounts with optional filtering. ```python scalekit_client.actions.list_connected_accounts( connection_name: Optional[str] = None, identifier: Optional[str] = None, provider: Optional[str] = None, connection_names: Optional[List[str]] = None, page_size: Optional[int] = None, page_token: Optional[str] = None, ) -> ListConnectedAccountsResponse ``` Example: ```python result = scalekit_client.actions.list_connected_accounts( connection_name="gmail", identifier="user_123", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_name` | `Optional[str]` | No | The connection name, as shown in **AgentKit** > **Connections**. | | `identifier` | `Optional[str]` | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `provider` | `Optional[str]` | No | Return only accounts for this app, such as `GMAIL` or `SLACK`. Exact match, in capitals as the account's `provider` shows it. | | `connection_names` | `Optional[List[str]]` | No | Connection names, exact match. Returns accounts on any of them, such as `["gmail", "github-connect"]`. Combine with `identifier` for one user. | | `page_size` | `Optional[int]` | No | Maximum number of connected accounts per page | | `page_token` | `Optional[str]` | No | `next_page_token` from a previous response, to fetch the next page | Returns `ListConnectedAccountsResponse`: The matching connected accounts, with `next_page_token` set when more pages remain. **Node.js SDK:** `scalekit.actions.listConnectedAccounts` List connected accounts with optional filters. ```ts scalekit.actions.listConnectedAccounts( params?: { connectionName?: string; identifier?: string; provider?: string; organizationId?: string; userId?: string; pageSize?: number; pageToken?: string; query?: string; connectionNames?: string[]; }, ): Promise ``` Example: ```ts const result = await scalekit.actions.listConnectedAccounts({ connectionName: "gmail", identifier: "user_123", }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connectionName` | `string` | No | The connection name, as shown in **AgentKit** > **Connections**. | | `identifier` | `string` | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `provider` | `string` | No | Return only accounts for this app, such as `GMAIL` or `SLACK`. Exact match, in capitals as the account's `provider` shows it. | | `organizationId` | `string` | No | Filter by organization ID. Returns only connected accounts associated with this organization. | | `userId` | `string` | No | Filter by user ID. Returns only connected accounts associated with this user. | | `pageSize` | `number` | No | Maximum number of connected accounts to return, up to 99. Defaults to 10 when omitted or 0. | | `pageToken` | `string` | No | Pagination token from a previous response. Use the next_page_token value from ListConnectedAccountsResponse to fetch the next page. | | `query` | `string` | No | Text search query to filter connected accounts by name, identifier, or other searchable fields. Case-insensitive. | | `connectionNames` | `string[]` | No | Filter by one or more connection names (exact match). Returns connected accounts belonging to any of the specified connections. Max 20 names per request. Cannot be combined with the `connector` field. | Returns `Promise`. **Used in** - [Manage connected accounts](https://docs.scalekit.com/agentkit/connected-accounts/) ## Search connected accounts `GET /api/v1/connected_accounts:search` Search for connected accounts in your environment using a text query that matches against identifiers, providers, or connectors. The search performs case-insensitive matching across account details. Returns paginated results with account status and authentication type information. **Query parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | Yes | Search term to match against connected account identifiers, providers, or connectors. Must be at least 3 characters. Case insensitive. | | `connection_id` | string | No | Connection ID to filter connected accounts. | | `page_size` | integer | No | Maximum number of connected accounts to return per page. Value must be between 1 and 30. | | `page_token` | string | No | Token from a previous response for pagination. Provide this to retrieve the next page of results. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `connected_accounts` | array of object | List of connected accounts matching the search query. Excludes sensitive authorization details. | | `connected_accounts.authorization_type` | string (enum) | Authorization mechanism type. One of: `OAUTH`, `API_KEY`, `BASIC_AUTH`, `BEARER_TOKEN`, `CUSTOM`, `BASIC`, `OAUTH_M2M`, `TRELLO_OAUTH1`, `GOOGLE_DWD`, `TRUSTED_IDP`, `SMART_FHIR`, `NO_AUTH`. | | `connected_accounts.connection_id` | string | Parent connection configuration reference. | | `connected_accounts.connector` | string | The connection name, as shown in **AgentKit** > **Connections**. | | `connected_accounts.id` | string | Unique connected account identifier. | | `connected_accounts.identifier` | string | Your app's ID for the user, the value passed when the account was created. | | `connected_accounts.is_org_wide_credential` | boolean | Whether this is the shared credential of an org-wide connection, which every user's tool calls on that connection use. `false` for a user's own account. | | `connected_accounts.last_used_at` | string | Last usage timestamp. | | `connected_accounts.provider` | string | The app the account connects to, such as `GMAIL` or `SLACK`. | | `connected_accounts.status` | string (enum) | Current connection status. One of: `ACTIVE`, `EXPIRED`, `PENDING_AUTH`, `PENDING_VERIFICATION`, `DISCONNECTED`. | | `connected_accounts.token_expires_at` | string | Token expiration timestamp. | | `connected_accounts.updated_at` | string | Last modification timestamp. | | `next_page_token` | string | Pagination token for the next page. Empty if this is the last page. | | `prev_page_token` | string | Pagination token for the previous page. Empty if this is the first page. | | `total_size` | integer | Total count of accounts matching the search query across all pages. | **Errors** - `400`: Invalid request - query parameter is too short (minimum 3 characters) or validation failed - `401`: Authentication required - missing or invalid access token **Request** ```bash curl -sS -G -X GET \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connected_accounts:search" \ -H "Authorization: Bearer $TOKEN" \ --data-urlencode "query=user_123" ``` **Response** ```json { "connected_accounts": [ { "id": "ca_24834495392086178", "connection_id": "conn_70219645518265104", "connector": "gmail", "identifier": "user_123", "provider": "GMAIL", "authorization_type": "OAUTH", "status": "ACTIVE", "is_org_wide_credential": false, "token_expires_at": "2026-10-02T15:30:00Z", "last_used_at": "2026-10-02T14:31:07Z", "updated_at": "2026-10-02T14:30:00Z" } ], "next_page_token": "", "prev_page_token": "", "total_size": 1 } ``` **Python:** Call this endpoint over REST, as the example shows. ```python import os import requests env_url = os.environ["SCALEKIT_ENVIRONMENT_URL"] token = requests.post( f"{env_url}/oauth/token", data={ "grant_type": "client_credentials", "client_id": os.environ["SCALEKIT_CLIENT_ID"], "client_secret": os.environ["SCALEKIT_CLIENT_SECRET"], }, ).json()["access_token"] response = requests.get( f"{env_url}/api/v1/connected_accounts:search", headers={"Authorization": f"Bearer {token}"}, params={"query": "user_123"}, ) response.raise_for_status() result = response.json() ``` **Node.js:** Call this endpoint over REST, as the example shows. ```ts const envUrl = process.env.SCALEKIT_ENVIRONMENT_URL!; const tokenResponse = await fetch(`${envUrl}/oauth/token`, { method: "POST", body: new URLSearchParams({ grant_type: "client_credentials", client_id: process.env.SCALEKIT_CLIENT_ID!, client_secret: process.env.SCALEKIT_CLIENT_SECRET!, }), }); const token = (await tokenResponse.json()).access_token; const params = new URLSearchParams({ query: "user_123" }); const response = await fetch(`${envUrl}/api/v1/connected_accounts:search?${params}`, { method: "GET", headers: { Authorization: `Bearer ${token}`, }, }); if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`); const result = await response.json(); ``` ## Update a connected account `PUT /api/v1/connected_accounts` Updates authentication credentials and configuration for an existing connected account. Modify OAuth tokens, refresh tokens, access scopes, or API configuration settings. Specify the account by ID, or by combination of organization/user, connector, and identifier. Returns the updated account with new token expiry and status information. To replace a static credential, send `authorization_details.static_auth.details` with the key that matches the connection's authentication type: `token` for bearer, `api_key` for API key, or `username` and `password` for basic auth. Sending a credential for a key-based account also makes it `ACTIVE`, so this is how you recover an `EXPIRED` one. **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connected_account` | object | Yes | Details of the connected account to update. | | `connected_account.api_config` | object | No | Updated JSON configuration for API-specific settings. Merges with existing configuration - only provided fields are modified. | | `connected_account.authorization_details` | object | No | Updated authentication credentials. Provide new OAuth tokens (e.g., after refresh) or updated static auth details. Only included fields will be modified. | | `connected_account.authorization_details.google_dwd` | object | No | Google Domain-Wide Delegation authentication — used for GOOGLE_DWD connections. Send only subject in requests; access_token, scopes, and token_expires_at are response-only. | | `connected_account.authorization_details.oauth_token` | object | No | OAuth 2.0 credentials. | | `connected_account.authorization_details.static_auth` | object | No | Static credentials, such as an API key. Send them in `details`. The keys depend on the connection's authentication type: `token` for bearer, `api_key` for API key, and `username` and `password` for basic auth. Providers that need several API key headers take one key per header. | | `connected_account.authorization_details.static_auth.details` | object | Yes | The credential, as a JSON object. Send the key that matches the connection's authentication type: `token` for bearer, `api_key` for API key, or `username` and `password` for basic auth. Add `domain` or `version` here when the provider needs them. | | `connected_account.authorization_details.static_auth.details.api_key` | string | No | For an **API key** connection: the key. Required. Providers that need several headers take one key per header instead, named by the provider's field name or header name. | | `connected_account.authorization_details.static_auth.details.password` | string | No | For a **basic auth** connection: the password. Leave it empty for providers that take a key as the username. | | `connected_account.authorization_details.static_auth.details.token` | string | No | For a **bearer** connection: the access token. Required. Scalekit sends it to the provider for you. | | `connected_account.authorization_details.static_auth.details.username` | string | No | For a **basic auth** connection: the username. Required. | | `connected_account.authorization_details.trusted_idp` | object | No | Credentials for a connection that signs in through a trusted identity provider, such as AWS Redshift. Send only `db_user`. Responses include `access_key_id` and `expiry`, never the secret key or session token. | | `connector` | string | No | The connection name, as shown in **AgentKit** > **Connections**. | | `id` | string | No | Unique identifier for the connected account to update. | | `identifier` | string | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `organization_id` | string | No | An organization ID to key the account by instead of `identifier`, such as a Scalekit organization ID. Ignored when `identifier` is set. | | `user_id` | string | No | A user ID that, with `organization_id`, keys the account to one user in that organization. Ignored when `identifier` is set. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `connected_account` | object | The updated connected account with refreshed credentials, new token expiry, and modified configuration settings. | | `connected_account.api_config` | object | Optional JSON configuration for connector-specific API settings such as rate limits, custom endpoints, or feature flags. | | `connected_account.authorization_details` | object | The account's credentials. Set the one that matches the connection's auth type - `oauth_token`, `static_auth`, `google_dwd` or `trusted_idp`. | | `connected_account.authorization_details.google_dwd` | object | Google Domain-Wide Delegation authentication — used for GOOGLE_DWD connections. Send only subject in requests; access_token, scopes, and token_expires_at are response-only. | | `connected_account.authorization_details.oauth_token` | object | OAuth 2.0 credentials. | | `connected_account.authorization_details.static_auth` | object | Static credentials, such as an API key. Send them in `details`. The keys depend on the connection's authentication type: `token` for bearer, `api_key` for API key, and `username` and `password` for basic auth. Providers that need several API key headers take one key per header. | | `connected_account.authorization_details.trusted_idp` | object | Credentials for a connection that signs in through a trusted identity provider, such as AWS Redshift. Send only `db_user`. Responses include `access_key_id` and `expiry`, never the secret key or session token. | | `connected_account.authorization_type` | string (enum) | Type of authorization mechanism used. Specifies whether this connection uses OAuth, API keys, bearer tokens, or other auth methods. One of: `OAUTH`, `API_KEY`, `BASIC_AUTH`, `BEARER_TOKEN`, `CUSTOM`, `BASIC`, `OAUTH_M2M`, `TRELLO_OAUTH1`, `GOOGLE_DWD`, `TRUSTED_IDP`, `SMART_FHIR`, `NO_AUTH`. | | `connected_account.connection_id` | string | Reference to the parent connection configuration. Links this account to a specific connector setup in your environment. | | `connected_account.connector` | string | The connection name, as shown in **AgentKit** > **Connections**. | | `connected_account.id` | string | Unique Scalekit-generated identifier for this connected account. Always prefixed with 'ca_'. | | `connected_account.identifier` | string | Your app's ID for the user, the value passed when the account was created. | | `connected_account.is_org_wide_credential` | boolean | Whether this is the shared credential of an org-wide connection, which every user's tool calls on that connection use. `false` for a user's own account. | | `connected_account.last_used_at` | string | Timestamp when this connected account was last used to make an API call. Useful for tracking active connections. | | `connected_account.provider` | string | The app the account connects to, such as `GMAIL` or `SLACK`. | | `connected_account.status` | string (enum) | Current status of the connected account. Indicates if the account is active, expired, pending authorization, or pending user identity verification. One of: `ACTIVE`, `EXPIRED`, `PENDING_AUTH`, `PENDING_VERIFICATION`, `DISCONNECTED`. | | `connected_account.token_expires_at` | string | Expiration timestamp for the access token. After this time, the token must be refreshed or re-authorized. | | `connected_account.updated_at` | string | Timestamp when this connected account was last modified. Updated whenever credentials or configuration changes. | **Errors** - `400`: Invalid request - missing required fields, invalid authorization details, or validation failed - `401`: Authentication required - missing or invalid access token - `404`: Connected account not found - the specified account does not exist **Request** ```bash curl -sS -X PUT \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connected_accounts" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "connector": "gmail", "identifier": "user_123", "connected_account": { "authorization_details": { "oauth_token": { "access_token": "[NEW ACCESS TOKEN]", "refresh_token": "[NEW REFRESH TOKEN]", "scopes": [ "https://www.googleapis.com/auth/gmail.readonly" ] } } } }' ``` **Request with a new API key** ```bash curl -sS -X PUT \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connected_accounts" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "connector": "api-key-connection", "identifier": "user_123", "connected_account": { "authorization_details": { "static_auth": { "details": { "api_key": "[NEW API KEY]" } } } } }' ``` ```python result = scalekit_client.actions.update_connected_account( connection_name="api-key-connection", identifier="user_123", authorization_details={ "static_auth": { "api_key": "[NEW API KEY]", }, }, ) ``` ```ts const result = await scalekit.actions.updateConnectedAccount({ connectionName: "api-key-connection", identifier: "user_123", authorizationDetails: { details: { case: "staticAuth", value: { details: { api_key: "[NEW API KEY]", }, }, }, }, }); ``` **Response** ```json { "connected_account": { "id": "ca_24834495392086178", "connection_id": "conn_70219645518265104", "connector": "gmail", "identifier": "user_123", "provider": "GMAIL", "authorization_type": "OAUTH", "status": "ACTIVE", "is_org_wide_credential": false, "token_expires_at": "2026-10-02T15:30:00Z", "last_used_at": "2026-10-02T14:31:07Z", "updated_at": "2026-10-02T14:30:00Z", "authorization_details": { "oauth_token": { "access_token": "[NEW ACCESS TOKEN]", "refresh_token": "[NEW REFRESH TOKEN]", "scopes": [ "https://www.googleapis.com/auth/gmail.readonly" ] } } } } ``` **Python SDK:** `scalekit_client.actions.update_connected_account` Update an existing connected account ```python scalekit_client.actions.update_connected_account( connection_name: str, identifier: str, authorization_details: Optional[Dict[str, Any]] = None, organization_id: Optional[str] = None, user_id: Optional[str] = None, connected_account_id: Optional[str] = None, api_config: Optional[Dict[str, Any]] = None, ) -> UpdateConnectedAccountResponse ``` Example: ```python result = scalekit_client.actions.update_connected_account( connection_name="gmail", identifier="user_123", authorization_details={ "oauth_token": { "access_token": "[NEW ACCESS TOKEN]", "refresh_token": "[NEW REFRESH TOKEN]", "scopes": ["https://www.googleapis.com/auth/gmail.readonly"], }, }, ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_name` | `str` | Yes | The connection name, as shown in **AgentKit** > **Connections**. | | `identifier` | `str` | Yes | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `authorization_details` | `Optional[Dict[str, Any]]` | No | The user's credentials as a dict: `{"oauth_token": {...}}` for OAuth tokens, or `{"static_auth": {...}}` for static credentials, holding the key the connection reads: `token` for bearer, `api_key` for API key, or `username` and `password` for basic auth. Unlike the REST body, `static_auth` has no `details` level. | | `organization_id` | `Optional[str]` | No | Organization ID | | `user_id` | `Optional[str]` | No | User ID | | `connected_account_id` | `Optional[str]` | No | Connected account ID | | `api_config` | `Optional[Dict[str, Any]]` | No | Optional API configuration for the connected account | Returns `UpdateConnectedAccountResponse`: The updated connected account details **Node.js SDK:** `scalekit.actions.updateConnectedAccount` Update an existing connected account. Requires either `connectedAccountId` or both `connectionName` + `identifier`. ```ts scalekit.actions.updateConnectedAccount( params: { connectionName?: string; identifier?: string; authorizationDetails?: UpdateConnectedAccount['authorizationDetails']; organizationId?: string; userId?: string; connectedAccountId?: string; apiConfig?: UpdateConnectedAccount['apiConfig']; }, ): Promise ``` Example: ```ts const result = await scalekit.actions.updateConnectedAccount({ connectionName: "gmail", identifier: "user_123", authorizationDetails: { details: { case: "oauthToken", value: { accessToken: "[NEW ACCESS TOKEN]", refreshToken: "[NEW REFRESH TOKEN]", scopes: ["https://www.googleapis.com/auth/gmail.readonly"], }, }, }, }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connectionName` | `string` | No | The connection name, as shown in **AgentKit** > **Connections**. | | `identifier` | `string` | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `authorizationDetails` | `UpdateConnectedAccount['authorizationDetails']` | No | The user's credentials: OAuth tokens as `{ case: "oauthToken", value }`, or static credentials as `{ case: "staticAuth", value }`, where `value.details` holds the key the connection reads: `token` for bearer, `api_key` for API key, or `username` and `password` for basic auth. | | `organizationId` | `string` | No | An organization ID to key the account by instead of `identifier`, such as a Scalekit organization ID. Ignored when `identifier` is set. | | `userId` | `string` | No | A user ID that, with `organization_id`, keys the account to one user in that organization. Ignored when `identifier` is set. | | `connectedAccountId` | `string` | No | Unique identifier for the connected account to update | | `apiConfig` | `UpdateConnectedAccount['apiConfig']` | No | Updated JSON configuration for API-specific settings. Merges with existing configuration - only provided fields are modified. | Returns `Promise`. Also called by `scalekit_client.actions.get_or_create_connected_account` (Python), `scalekit.actions.getOrCreateConnectedAccount` (Node.js). ## Get a connected account's credentials `GET /api/v1/connected_accounts/auth` Returns a connected account with its credentials, the user's OAuth tokens or the API key or other values they entered, so you can call the app yourself. If the OAuth access token has expired, Scalekit refreshes it first. Scalekit returns credentials only when credential access is turned on for your environment; to turn it on, contact Scalekit support. Until then, the response is the same as [Get a connected account](https://docs.scalekit.com/agentkit/reference/connected-accounts/get-a-connected-account/), with no `authorization_details`. **Query parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connector` | string | No | The connection name, as shown in **AgentKit** > **Connections**. | | `id` | string | No | Unique identifier for the connected account. | | `identifier` | string | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `organization_id` | string | No | An organization ID to key the account by instead of `identifier`, such as a Scalekit organization ID. Ignored when `identifier` is set. | | `user_id` | string | No | A user ID that, with `organization_id`, keys the account to one user in that organization. Ignored when `identifier` is set. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `connected_account` | object | The connected account. | | `connected_account.api_config` | object | Optional JSON configuration for connector-specific API settings such as rate limits, custom endpoints, or feature flags. | | `connected_account.authorization_details` | object | The account's credentials, in the shape for its auth type. Returned only when credential access is turned on for your environment. | | `connected_account.authorization_details.google_dwd` | object | Google Domain-Wide Delegation authentication — used for GOOGLE_DWD connections. Send only subject in requests; access_token, scopes, and token_expires_at are response-only. | | `connected_account.authorization_details.oauth_token` | object | OAuth 2.0 credentials. | | `connected_account.authorization_details.static_auth` | object | Static credentials, such as an API key. Send them in `details`. The keys depend on the connection's authentication type: `token` for bearer, `api_key` for API key, and `username` and `password` for basic auth. Providers that need several API key headers take one key per header. | | `connected_account.authorization_details.trusted_idp` | object | Credentials for a connection that signs in through a trusted identity provider, such as AWS Redshift. Send only `db_user`. Responses include `access_key_id` and `expiry`, never the secret key or session token. | | `connected_account.authorization_type` | string (enum) | Type of authorization mechanism used. Specifies whether this connection uses OAuth, API keys, bearer tokens, or other auth methods. One of: `OAUTH`, `API_KEY`, `BASIC_AUTH`, `BEARER_TOKEN`, `CUSTOM`, `BASIC`, `OAUTH_M2M`, `TRELLO_OAUTH1`, `GOOGLE_DWD`, `TRUSTED_IDP`, `SMART_FHIR`, `NO_AUTH`. | | `connected_account.connection_id` | string | Reference to the parent connection configuration. Links this account to a specific connector setup in your environment. | | `connected_account.connector` | string | The connection name, as shown in **AgentKit** > **Connections**. | | `connected_account.id` | string | Unique Scalekit-generated identifier for this connected account. Always prefixed with 'ca_'. | | `connected_account.identifier` | string | Your app's ID for the user, the value passed when the account was created. | | `connected_account.is_org_wide_credential` | boolean | Whether this is the shared credential of an org-wide connection, which every user's tool calls on that connection use. `false` for a user's own account. | | `connected_account.last_used_at` | string | Timestamp when this connected account was last used to make an API call. Useful for tracking active connections. | | `connected_account.provider` | string | The app the account connects to, such as `GMAIL` or `SLACK`. | | `connected_account.status` | string (enum) | Current status of the connected account. Indicates if the account is active, expired, pending authorization, or pending user identity verification. One of: `ACTIVE`, `EXPIRED`, `PENDING_AUTH`, `PENDING_VERIFICATION`, `DISCONNECTED`. | | `connected_account.token_expires_at` | string | Expiration timestamp for the access token. After this time, the token must be refreshed or re-authorized. | | `connected_account.updated_at` | string | Timestamp when this connected account was last modified. Updated whenever credentials or configuration changes. | **Errors** - `400`: Invalid request - missing required query parameters - `401`: Authentication required - missing or invalid access token - `404`: Connected account not found - no account matches the specified criteria **Request** ```bash curl -sS -G -X GET \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connected_accounts/auth" \ -H "Authorization: Bearer $TOKEN" \ --data-urlencode "connector=gmail" \ --data-urlencode "identifier=user_123" ``` **Response** ```json { "connected_account": { "id": "ca_24834495392086178", "connection_id": "conn_70219645518265104", "connector": "gmail", "identifier": "user_123", "provider": "GMAIL", "authorization_type": "OAUTH", "status": "ACTIVE", "is_org_wide_credential": false, "token_expires_at": "2026-10-02T15:30:00Z", "last_used_at": "2026-10-02T14:31:07Z", "updated_at": "2026-10-02T14:30:00Z", "authorization_details": { "oauth_token": { "access_token": "[ACCESS TOKEN]", "refresh_token": "[REFRESH TOKEN]", "scopes": [ "https://www.googleapis.com/auth/gmail.readonly" ] } } } } ``` **Python SDK:** `scalekit_client.actions.get_connected_account` Get connected account authorization details by identifier ```python scalekit_client.actions.get_connected_account( connection_name: Optional[str] = None, identifier: Optional[str] = None, connected_account_id: Optional[str] = None, ) -> GetConnectedAccountAuthResponse ``` Example: ```python result = scalekit_client.actions.get_connected_account( connection_name="gmail", identifier="user_123", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_name` | `Optional[str]` | No | The connection name, as shown in **AgentKit** > **Connections**. | | `identifier` | `Optional[str]` | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `connected_account_id` | `Optional[str]` | No | Connected account ID | Returns `GetConnectedAccountAuthResponse`: The connected account details **Node.js SDK:** `scalekit.actions.getConnectedAccount` Get connected account authorization details. Requires either `connectedAccountId` or both `connectionName` + `identifier`. ```ts scalekit.actions.getConnectedAccount( params: { connectionName?: string; identifier?: string; connectedAccountId?: string; organizationId?: string; userId?: string; }, ): Promise ``` Example: ```ts const result = await scalekit.actions.getConnectedAccount({ connectionName: "gmail", identifier: "user_123", }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connectionName` | `string` | No | The connection name, as shown in **AgentKit** > **Connections**. | | `identifier` | `string` | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `connectedAccountId` | `string` | No | Unique identifier for the connected account | | `organizationId` | `string` | No | An organization ID to key the account by instead of `identifier`, such as a Scalekit organization ID. Ignored when `identifier` is set. | | `userId` | `string` | No | A user ID that, with `organization_id`, keys the account to one user in that organization. Ignored when `identifier` is set. | Returns `Promise`. **Used in** - [Build a multi-user GitHub PR summarizer agent](https://docs.scalekit.com/cookbooks/render-github-pr-summarizer/) - [Build an agent that books meetings and drafts emails](https://docs.scalekit.com/cookbooks/schedule-meeting-and-draft-email/) - [Call any API](https://docs.scalekit.com/agentkit/tools/custom-tools/) - [Quickstart](https://docs.scalekit.com/agentkit/quickstart/) - [Troubleshoot connection and OAuth errors](https://docs.scalekit.com/agentkit/troubleshooting/) Also called by `scalekit_client.actions.get_or_create_connected_account` (Python), `scalekit.actions.getOrCreateConnectedAccount` (Node.js). ## Delete a connected account `POST /api/v1/connected_accounts:delete` Deletes the account and the credentials Scalekit stores for it. It doesn't revoke the user's grant at the app. Identify the account by ID, or by its connector and identifier. This can't be undone. **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connector` | string | No | The connection name, as shown in **AgentKit** > **Connections**. | | `id` | string | No | Unique identifier for the connected account to delete. | | `identifier` | string | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `organization_id` | string | No | An organization ID to key the account by instead of `identifier`, such as a Scalekit organization ID. Ignored when `identifier` is set. | | `user_id` | string | No | A user ID that, with `organization_id`, keys the account to one user in that organization. Ignored when `identifier` is set. | **Errors** - `400`: Invalid request - malformed parameters or validation failed. Error code `MCP_SERVER_EXISTS_FOR_CONNECTED_ACCOUNT` when a Virtual MCP server instance for this user still uses the account; delete that Virtual MCP server first. - `401`: Authentication required - missing or invalid access token - `404`: Connected account not found - the specified account does not exist **Request** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connected_accounts:delete" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "connector": "gmail", "identifier": "user_123" }' ``` **Python SDK:** `scalekit_client.actions.delete_connected_account` Delete a connected account ```python scalekit_client.actions.delete_connected_account( connection_name: Optional[str] = None, identifier: Optional[str] = None, connected_account_id: Optional[str] = None, ) -> DeleteConnectedAccountResponse ``` Example: ```python result = scalekit_client.actions.delete_connected_account( connection_name="gmail", identifier="user_123", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_name` | `Optional[str]` | No | The connection name, as shown in **AgentKit** > **Connections**. | | `identifier` | `Optional[str]` | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `connected_account_id` | `Optional[str]` | No | Connected account ID | Returns `DeleteConnectedAccountResponse`: An empty response. **Node.js SDK:** `scalekit.actions.deleteConnectedAccount` Delete a connected account. Requires either `connectedAccountId` or both `connectionName` + `identifier`. ```ts scalekit.actions.deleteConnectedAccount( params: { connectionName?: string; identifier?: string; connectedAccountId?: string; organizationId?: string; userId?: string; }, ): Promise ``` Example: ```ts const result = await scalekit.actions.deleteConnectedAccount({ connectionName: "gmail", identifier: "user_123", }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connectionName` | `string` | No | The connection name, as shown in **AgentKit** > **Connections**. | | `identifier` | `string` | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `connectedAccountId` | `string` | No | Unique identifier for the connected account to delete | | `organizationId` | `string` | No | An organization ID to key the account by instead of `identifier`, such as a Scalekit organization ID. Ignored when `identifier` is set. | | `userId` | `string` | No | A user ID that, with `organization_id`, keys the account to one user in that organization. Ignored when `identifier` is set. | Returns `Promise`. **Used in** - [Manage connected accounts](https://docs.scalekit.com/agentkit/connected-accounts/) --- Source: https://docs.scalekit.com/agentkit/reference/authorization.md # Authorization Send a user to connect their account, then confirm they are the user your app meant. The steps and when to use each option are in [Authorize a user](https://docs.scalekit.com/agentkit/tools/authorize/) and [Verify users](https://docs.scalekit.com/agentkit/user-verification/). One page per endpoint, each with its own markdown copy: | Endpoint | Request | What it does | Python SDK | Node.js SDK | | --- | --- | --- | --- | --- | | [Get an authorization link](https://docs.scalekit.com/agentkit/reference/authorization/get-an-authorization-link.md) | `POST /api/v1/connected_accounts/magic_link` | Create the one-time link a user opens to connect their account. | `actions.get_authorization_link` | `actions.getAuthorizationLink` | | [Verify the user](https://docs.scalekit.com/agentkit/reference/authorization/verify-the-user.md) | `POST /api/v1/connected_accounts/user/verify` | Confirm that the user who approved access is the user your app meant. | `actions.verify_connected_account_user` | `actions.verifyConnectedAccountUser` | ## Get an authorization link `POST /api/v1/connected_accounts/magic_link` Creates a one-time authorization link that takes the user to the app's consent screen, or to a form for their API key, for one connection. If the user has no connected account for the connection yet, the call creates it first. When the user finishes, the account becomes `ACTIVE`. With user verification on, it becomes `PENDING_VERIFICATION` instead, and stays that way until you call [Verify the user](https://docs.scalekit.com/agentkit/reference/authorization/verify-the-user/). The link expires 5 minutes after you create it, so create a new one each time you send it. The endpoint path and the `connected_account.magic_link_generated` event call it a magic link. **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connector` | string | No | The connection name, as shown in **AgentKit** > **Connections**. | | `id` | string | No | Unique identifier for the connected account. | | `identifier` | string | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `organization_id` | string | No | An organization ID to key the account by instead of `identifier`, such as a Scalekit organization ID. Ignored when `identifier` is set. | | `state` | string | No | A value of your own, such as a session ID. Scalekit adds it to the request it sends to `user_verify_url`, so your app can check that the request is the one it started. | | `user_id` | string | No | A user ID that, with `organization_id`, keys the account to one user in that organization. Ignored when `identifier` is set. | | `user_verify_url` | string | No | Your app's URL that Scalekit sends the user to after they approve access, to confirm they are the user your app meant. Required when the environment verifies users with a custom verifier. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `expiry` | string | When the link stops working: 5 minutes after you created it. | | `link` | string | The authorization link to send the user to. It's on your environment's domain and works once. | **Errors** - `400`: Invalid request - missing required parameters, or a malformed connected account ID - `401`: Authentication required - missing or invalid access token - `404`: Not found - no connection with this name exists in the environment. Error code `RESOURCE_NOT_FOUND`. **Request** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connected_accounts/magic_link" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "connector": "gmail", "identifier": "user_123", "user_verify_url": "https://app.example.com/verify", "state": "[SESSION STATE]" }' ``` **Response** ```json { "link": "https://your-env.scalekit.dev/magicLink/7f0c2b9e-4d1a-4c3e-9b8f-2a6d5e1c3f40_o", "expiry": "2026-10-02T14:35:00Z" } ``` **Python SDK:** `scalekit_client.actions.get_authorization_link` Creates the link a user opens to connect their account. ```python scalekit_client.actions.get_authorization_link( identifier: Optional[str] = None, connection_name: Optional[str] = None, connected_account_id: Optional[str] = None, state: Optional[str] = None, user_verify_url: Optional[str] = None, ) -> MagicLinkResponse ``` Example: ```python result = scalekit_client.actions.get_authorization_link( identifier="user_123", connection_name="gmail", state="[SESSION STATE]", user_verify_url="https://app.example.com/verify", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `identifier` | `Optional[str]` | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `connection_name` | `Optional[str]` | No | The connection name, as shown in **AgentKit** > **Connections**. | | `connected_account_id` | `Optional[str]` | No | Connected account ID | | `state` | `Optional[str]` | No | A value of your own, such as a session ID. Scalekit adds it to the request it sends to `user_verify_url`, so your app can check that the request is the one it started. | | `user_verify_url` | `Optional[str]` | No | Your app's URL that Scalekit sends the user to after they approve access, to confirm they are the user your app meant. Required when the environment verifies users with a custom verifier. | Returns `MagicLinkResponse`: The authorization `link` and its `expiry`. **Node.js SDK:** `scalekit.actions.getAuthorizationLink` Creates the link a user opens to connect their account. ```ts scalekit.actions.getAuthorizationLink( params: { connectionName?: string; identifier?: string; connectedAccountId?: string; organizationId?: string; userId?: string; state?: string; userVerifyUrl?: string; }, ): Promise ``` Example: ```ts const result = await scalekit.actions.getAuthorizationLink({ connectionName: "gmail", identifier: "user_123", state: "[SESSION STATE]", userVerifyUrl: "https://app.example.com/verify", }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connectionName` | `string` | No | The connection name, as shown in **AgentKit** > **Connections**. | | `identifier` | `string` | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `connectedAccountId` | `string` | No | Unique identifier for the connected account | | `organizationId` | `string` | No | An organization ID to key the account by instead of `identifier`, such as a Scalekit organization ID. Ignored when `identifier` is set. | | `userId` | `string` | No | A user ID that, with `organization_id`, keys the account to one user in that organization. Ignored when `identifier` is set. | | `state` | `string` | No | A value of your own, such as a session ID. Scalekit adds it to the request it sends to `user_verify_url`, so your app can check that the request is the one it started. | | `userVerifyUrl` | `string` | No | Your app's URL that Scalekit sends the user to after they approve access, to confirm they are the user your app meant. Required when the environment verifies users with a custom verifier. | Returns `Promise`. **Used in** - [Apify Actor with per-user OAuth via Scalekit](https://docs.scalekit.com/cookbooks/apify-actor-per-user-oauth/) - [Authorize a user](https://docs.scalekit.com/agentkit/tools/authorize/) - [Build a daily briefing agent with Vercel AI SDK and Scalekit AgentKit](https://docs.scalekit.com/cookbooks/daily-briefing-agent/) - [Build a Mastra agent with Scalekit AgentKit tools](https://docs.scalekit.com/cookbooks/mastra-agentkit/) - [Build a multi-agent email triage crew with CrewAI](https://docs.scalekit.com/cookbooks/crewai-agentkit-email-triage/) - [Build a multi-user GitHub PR summarizer agent](https://docs.scalekit.com/cookbooks/render-github-pr-summarizer/) - [Build an agent that books meetings and drafts emails](https://docs.scalekit.com/cookbooks/schedule-meeting-and-draft-email/) - [Claude Managed Agents](https://docs.scalekit.com/agentkit/examples/claude-managed-agents/) - [FastRouter + Scalekit tool calling](https://docs.scalekit.com/cookbooks/fastrouter-agentkit-tool-calling/) - [Manage connected accounts](https://docs.scalekit.com/agentkit/connected-accounts/) - [Migrate from Composio to Scalekit](https://docs.scalekit.com/agentkit/advanced/migrate-from-composio/) - [Quickstart](https://docs.scalekit.com/agentkit/quickstart/) - [Trace AgentKit tool calls in LangSmith](https://docs.scalekit.com/cookbooks/langsmith-tracing-agentkit/) - [Troubleshoot connection and OAuth errors](https://docs.scalekit.com/agentkit/troubleshooting/) - [Verify users](https://docs.scalekit.com/agentkit/user-verification/) ## Verify the user `POST /api/v1/connected_accounts/user/verify` Confirms that the user who just authorized a connection is the user your app meant, then makes their connected account `ACTIVE`. When your environment verifies users with a custom verifier, Scalekit sends the user to your `user_verify_url` after they approve access, with an `auth_request_id` query parameter. Call this endpoint from your server with that `auth_request_id` and the identifier of the user signed in to your app, then send the user to `post_user_verify_redirect_url`. If the identifier doesn't match the one the [authorization link](https://docs.scalekit.com/agentkit/reference/authorization/get-an-authorization-link/) was created for, the call returns `403` and the account stays `PENDING_VERIFICATION`. **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `auth_request_id` | string | Yes | The `auth_request_id` query parameter from the request Scalekit sent to your `user_verify_url`. Pass it as it is. | | `identifier` | string | Yes | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `post_user_verify_redirect_url` | string | Where to send the user next, to finish connecting. | **Errors** - `400`: Invalid request - missing or malformed fields - `401`: Unauthorized - invalid or missing access token - `403`: Forbidden - identifier mismatch - `404`: Not found - no pending flow for the given auth_request_id or already consumed **Request** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connected_accounts/user/verify" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "auth_request_id": "[AUTH REQUEST ID]", "identifier": "user_123" }' ``` **Response** ```json { "post_user_verify_redirect_url": "https://env1.example.com/connect/success" } ``` **Python SDK:** `scalekit_client.actions.verify_connected_account_user` Confirms the user who authorized is the user your app meant. ```python scalekit_client.actions.verify_connected_account_user( auth_request_id: str, identifier: str, ) -> VerifyConnectedAccountUserResponse ``` Example: ```python result = scalekit_client.actions.verify_connected_account_user( auth_request_id="[AUTH REQUEST ID]", identifier="user_123", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `auth_request_id` | `str` | Yes | The `auth_request_id` query parameter from the request Scalekit sent to your `user_verify_url`. Pass it as it is. | | `identifier` | `str` | Yes | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | Returns `VerifyConnectedAccountUserResponse`: The `post_user_verify_redirect_url` to send the user to. **Node.js SDK:** `scalekit.actions.verifyConnectedAccountUser` Confirms the user who authorized is the user your app meant. Call it from your server when Scalekit sends the user to your `userVerifyUrl`, with the `authRequestId` from that request and the identifier of the user signed in to your app. The connected account becomes `ACTIVE`. ```ts scalekit.actions.verifyConnectedAccountUser( params: { authRequestId: string; identifier: string; }, ): Promise ``` Example: ```ts const result = await scalekit.actions.verifyConnectedAccountUser({ authRequestId: "[AUTH REQUEST ID]", identifier: "user_123", }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `authRequestId` | `string` | Yes | The `auth_request_id` query parameter from the request Scalekit sent to your `user_verify_url`. Pass it as it is. | | `identifier` | `string` | Yes | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | Returns `Promise`. **Used in** - [Build a multi-user GitHub PR summarizer agent](https://docs.scalekit.com/cookbooks/render-github-pr-summarizer/) - [FastRouter + Scalekit tool calling](https://docs.scalekit.com/cookbooks/fastrouter-agentkit-tool-calling/) - [Verify users](https://docs.scalekit.com/agentkit/user-verification/) --- Source: https://docs.scalekit.com/agentkit/reference/tools.md # Tools Find the tools your agent can use and call them as a user. [Use built-in tools](https://docs.scalekit.com/agentkit/tools/scalekit-optimized-tools/) shows the whole flow, and each [connector](https://docs.scalekit.com/agentkit/connectors/) lists its tools and their inputs. To call an endpoint of the app's API that no tool covers, send it through the API proxy at `/proxy/`, as [Call any API](https://docs.scalekit.com/agentkit/tools/custom-tools/) shows. One page per endpoint, each with its own markdown copy: | Endpoint | Request | What it does | Python SDK | Node.js SDK | | --- | --- | --- | --- | --- | | [Execute a tool](https://docs.scalekit.com/agentkit/reference/tools/execute-a-tool.md) | `POST /api/v1/execute_tool` | Run one tool as a user, with that user's credentials. | `actions.execute_tool` | `actions.executeTool` | | [List tools](https://docs.scalekit.com/agentkit/reference/tools/list-tools.md) | `GET /api/v1/tools` | List the tools in your environment, filtered by connection, provider or name. | `actions.list_tools` | `actions.listTools` | | [List a user's scoped tools (beta)](https://docs.scalekit.com/agentkit/reference/tools/list-scoped-tools.md) | `GET /api/v1/tools/scoped` | List the tools one user can call, by connection, provider or tool name. | `tools.list_scoped_tools` | `tools.listScopedTools` | | [List tools available to a user (beta)](https://docs.scalekit.com/agentkit/reference/tools/list-available-tools.md) | `GET /api/v1/tools/available` | List every tool a user's connected accounts can call. | REST only | `tools.listAvailableTools` | | [Search tools (beta)](https://docs.scalekit.com/agentkit/reference/tools/search-tools.md) | `POST /api/v1/tools:search` | Rank tools by how well they fit a task described in plain language. | `tools.search_tools` | `tools.searchTools` | ## Execute a tool `POST /api/v1/execute_tool` Runs one tool as a user, with that user's credentials for the connection. Identify the account with `connector` and `identifier`, or with `connected_account_id`, and pass the tool's inputs in `params`; each connector page lists its tools and their inputs. When the connected account is `EXPIRED`, the call returns `401` with `TOOL_ERROR`; when it's otherwise not `ACTIVE`, `400` with `INVALID_ARGUMENT`. Either way, send the user an [authorization link](https://docs.scalekit.com/agentkit/reference/authorization/get-an-authorization-link/). [Errors and rate limits](https://docs.scalekit.com/agentkit/reference/errors/) explains each tool error code. **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `tool_name` | string | Yes | Name of the tool to execute. | | `agent_run_id` | string | No | Customer-supplied identifier grouping multiple tool calls into a single agent run. Useful for correlating logs across an agentic workflow. | | `connected_account_id` | string | No | The unique ID of the connected account. Use this to directly identify the connected account instead of using identifier + connector combination. | | `connector` | string | No | The connection name, as shown in **AgentKit** > **Connections**. | | `identifier` | string | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `organization_id` | string | No | The organization ID to scope the connected account lookup. Use this to narrow down the search when the same identifier exists across multiple organizations. | | `params` | object | No | JSON object containing the parameters required for tool execution. The structure depends on the specific tool being executed. | | `user_id` | string | No | The user ID to scope the connected account lookup. Use this to narrow down the search when the same identifier exists across multiple users. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `data` | object | The tool's output: the app's response, as JSON. | | `execution_id` | string | Unique identifier for the tool execution. | **Errors** - `400`: Invalid request - error code `INVALID_ARGUMENT` when the tool name is missing, the input doesn't match the tool's schema, the connected account belongs to a different app than the tool, or the account isn't `ACTIVE`. Error code `TOOL_ERROR` when the app rejected the call; `tool_error_info.tool_error_code` is `INVALID_ARGUMENT`, `EXECUTION_ERROR`, `INTERNAL_ERROR` or, for a tool from an app's MCP server, `TOOL_ERROR`, and `tool_error_message` has the app's message. - `401`: Error code `UNAUTHENTICATED` when your access token is missing, invalid or expired; get a new token and retry. Error code `TOOL_ERROR` with `tool_error_code` `REAUTHENTICATION_NEEDED` or `UNAUTHENTICATED` when the user's access to the app was revoked or expired; send the user a new authorization link. - `403`: Error code `TOOL_ERROR` with `tool_error_code` `FORBIDDEN` or `PERMISSION_DENIED` - the app refused the call. The account's credentials are valid but lack the scope this tool needs. The connected account stays `ACTIVE`; grant the missing scope on the connection, have the user authorize again, then retry. - `404`: Not found - the tool or the connected account doesn't exist. Error code `TOOL_ERROR` with `tool_error_code` `RESOURCE_NOT_FOUND` when the app couldn't find what the call refers to, such as an event ID. - `429`: Error code `TOOL_ERROR` with `tool_error_code` `RATE_LIMITED` - the app rate-limited the call. Back off and retry. - `500`: Error code `TOOL_ERROR` with `tool_error_code` `INTERNAL_ERROR` when Scalekit couldn't run the tool, for example because the connection's auth type isn't supported for this tool. For a tool from an app's MCP server, `tool_error_code` `TOOL_ERROR` when that server failed, rate-limited the call or couldn't be reached; its status is in `tool_error_message`. A retry can run the tool twice, so retry only tools that are safe to repeat, and quote `execution_id` to support. **Request** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/execute_tool" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "tool_name": "gmail_fetch_mails", "connector": "gmail", "identifier": "user_123", "params": { "query": "is:unread", "max_results": 5 } }' ``` **Response** ```json { "data": { "messages": [ { "id": "19a4f2c8e6b1d037", "threadId": "19a4f2c8e6b1d037", "labelIds": [ "UNREAD", "IMPORTANT", "INBOX" ], "snippet": "Here are the notes from today's planning call", "payload": { "mimeType": "multipart/alternative", "headers": [ { "name": "From", "value": "Dana Lee " }, { "name": "Subject", "value": "Notes from the planning call" }, { "name": "Date", "value": "Fri, 2 Oct 2026 13:52:10 +0000" } ] }, "sizeEstimate": 6421, "historyId": "2174983", "internalDate": "1790949130000" } ], "page_token": "" }, "execution_id": "6f1c2e4a-a0b3-11f1-9c2a-0242ac120002" } ``` **Python SDK:** `scalekit_client.actions.execute_tool` Execute a tool with the given parameters. ```python scalekit_client.actions.execute_tool( tool_input: ToolInput, tool_name: str, identifier: Optional[str] = None, tool_request: Optional[ToolRequest] = None, connected_account_id: Optional[str] = None, connection_name: Optional[str] = None, ) -> ExecuteToolResponse ``` Example: ```python result = scalekit_client.actions.execute_tool( tool_input={"query": "is:unread", "max_results": 5}, tool_name="gmail_fetch_mails", identifier="user_123", connection_name="gmail", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `tool_input` | `ToolInput` | Yes | Input data for the tool execution | | `tool_name` | `str` | Yes | Name of the tool to execute | | `identifier` | `Optional[str]` | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `connected_account_id` | `Optional[str]` | No | ID of the connected account to use. Provide this OR the (identifier + connection_name) pair — not both. | | `connection_name` | `Optional[str]` | No | The connection name, as shown in **AgentKit** > **Connections**. | Returns `ExecuteToolResponse`: The tool's output in `data`, and the `execution_id`. **Node.js SDK:** `scalekit.actions.executeTool` Execute a tool on behalf of a connected account. Identify the account with `connector` and `identifier`, or with `connectedAccountId`. ```ts scalekit.actions.executeTool( params: { toolName: string; toolInput: Record; identifier?: string; connectedAccountId?: string; connector?: string; organizationId?: string; userId?: string; }, ): Promise ``` Example: ```ts const result = await scalekit.actions.executeTool({ toolName: "gmail_fetch_mails", toolInput: { query: "is:unread", max_results: 5 }, identifier: "user_123", connector: "gmail", }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `toolName` | `string` | Yes | Name of the tool to execute | | `toolInput` | `Record` | Yes | JSON object containing the parameters required for tool execution. The structure depends on the specific tool being executed. | | `identifier` | `string` | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `connectedAccountId` | `string` | No | The unique ID of the connected account. Use this to directly identify the connected account instead of using identifier + connector combination. | | `connector` | `string` | No | The connection name, as shown in **AgentKit** > **Connections**. | | `organizationId` | `string` | No | The organization ID to scope the connected account lookup. Use this to narrow down the search when the same identifier exists across multiple organizations. | | `userId` | `string` | No | The user ID to scope the connected account lookup. Use this to narrow down the search when the same identifier exists across multiple users. | Returns `Promise`. **Used in** - [Anthropic](https://docs.scalekit.com/agentkit/examples/anthropic/) - [Build a daily briefing agent with Vercel AI SDK and Scalekit AgentKit](https://docs.scalekit.com/cookbooks/daily-briefing-agent/) - [Build a LiveKit voice agent with Scalekit AgentKit tools](https://docs.scalekit.com/cookbooks/livekit-agentkit-voice-tool-calling/) - [Build a Mastra agent with Scalekit AgentKit tools](https://docs.scalekit.com/cookbooks/mastra-agentkit/) - [Build a multi-user GitHub PR summarizer agent](https://docs.scalekit.com/cookbooks/render-github-pr-summarizer/) - [Build an agent that books meetings and drafts emails](https://docs.scalekit.com/cookbooks/schedule-meeting-and-draft-email/) - [Call your connector](https://docs.scalekit.com/agentkit/bring-your-own-connector/making-tool-calls/) - [FastRouter + Scalekit tool calling](https://docs.scalekit.com/cookbooks/fastrouter-agentkit-tool-calling/) - [LangGraph](https://docs.scalekit.com/agentkit/examples/langgraph/) - [Migrate from Composio to Scalekit](https://docs.scalekit.com/agentkit/advanced/migrate-from-composio/) - [OpenAI](https://docs.scalekit.com/agentkit/examples/openai/) - [OpenAI Agents SDK](https://docs.scalekit.com/agentkit/examples/openai-agents-sdk/) - [Quickstart](https://docs.scalekit.com/agentkit/quickstart/) - [Use built-in tools](https://docs.scalekit.com/agentkit/tools/scalekit-optimized-tools/) - [Vercel AI SDK](https://docs.scalekit.com/agentkit/examples/vercel-ai/) Every [connector page](https://docs.scalekit.com/agentkit/connectors/) shows this call with that connector's tools and their inputs. ## List tools `GET /api/v1/tools` Lists the tools in your environment, filtered by connection, provider, tool name or a text query. Use it for every tool in the environment; for the tools one user can call, use [List a user's scoped tools](https://docs.scalekit.com/agentkit/reference/tools/list-scoped-tools/). With `filter.summary` set to `true`, the response has only `tool_names`. **Query parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `filter.connected_account_id` | string | No | Connected account ID. Alternative to filter.identifier + filter.connector for directly identifying the connected account whose custom MCP tools should be included. | | `filter.connector` | string | No | The connection name, as shown in **AgentKit** > **Connections**. | | `filter.identifier` | string | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `filter.organization_id` | string | No | Organization ID to scope the connected account lookup. | | `filter.provider` | string | No | The app, such as `GMAIL`. | | `filter.query` | string | No | Full-text search query to match tools by name or description (e.g., "gmail get attachment"). | | `filter.summary` | boolean | No | Return only tool names instead of full tool details. | | `filter.tool_name` | array of string | No | Filter by one or more tool names. | | `filter.user_id` | string | No | User ID to scope the connected account lookup. | | `page_size` | integer | No | Maximum number of tools to return per page. | | `page_token` | string | No | Token from a previous response for pagination. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `next_page_token` | string | Token for fetching the next page of tools. | | `prev_page_token` | string | Token for fetching the previous page of tools. | | `tool_names` | array of string | List of tool names, returned when filter.summary is true. | | `tools` | array of object | List of tools, returned when filter.summary is false or omitted. | | `tools.definition` | object | Tool definition in structured format. | | `tools.id` | string | Unique ID of the tool. Immutable and read-only. | | `tools.is_default` | boolean | Marks this tool as the default version for the combination. Read-only. | | `tools.metadata` | object | Additional metadata about the tool. | | `tools.provider` | string | Provider name (e.g. GOOGLE). Read-only. | | `tools.tags` | array of string | Tags for categorization or filtering. | | `tools.updated_at` | string | Timestamp when the tool was last updated. Read-only. | | `total_size` | integer | Total number of tools matching the query. | **Errors** - `400`: Invalid request - malformed filter or pagination parameters - `401`: Authentication required - missing or invalid access token **Request** ```bash curl -sS -G -X GET \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/tools" \ -H "Authorization: Bearer $TOKEN" \ --data-urlencode "filter.connector=gmail" \ --data-urlencode "filter.summary=true" \ --data-urlencode "page_size=5" ``` **Response** ```json { "tool_names": [ "gmail_batch_delete_messages", "gmail_batch_modify_messages", "gmail_create_draft", "gmail_create_filter", "gmail_create_label" ], "next_page_token": "eyJhZnRlciI6InRvbF84NjE0NzQwMzI5MTU4MzMyNyJ9", "prev_page_token": "", "total_size": 48 } ``` **Python SDK:** `scalekit_client.actions.list_tools` Lists the tools in your environment. Pass `connection_name` and `identifier`, or `connected_account_id`, to include the custom MCP tools of that connected account. ```python scalekit_client.actions.list_tools( connection_name: Optional[str] = None, identifier: Optional[str] = None, provider: Optional[str] = None, tool_name: Optional[List[str]] = None, query: Optional[str] = None, organization_id: Optional[str] = None, user_id: Optional[str] = None, connected_account_id: Optional[str] = None, summary: Optional[bool] = None, page_size: Optional[int] = None, page_token: Optional[str] = None, ) -> ListToolsResponse ``` Example: ```python result = scalekit_client.actions.list_tools( connection_name="gmail", summary=True, page_size=5, ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_name` | `Optional[str]` | No | The connection name, as shown in **AgentKit** > **Connections**. | | `identifier` | `Optional[str]` | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `provider` | `Optional[str]` | No | The app, such as `GMAIL`. | | `tool_name` | `Optional[List[str]]` | No | Filter to specific tool names | | `query` | `Optional[str]` | No | Free-form search query across tool metadata | | `organization_id` | `Optional[str]` | No | Organization ID to scope the connected-account lookup | | `user_id` | `Optional[str]` | No | User ID to scope the connected-account lookup | | `connected_account_id` | `Optional[str]` | No | Direct connected account ID, as an alternative to identifier + connection_name | | `summary` | `Optional[bool]` | No | Set to `True` to return only tool names, in `tool_names`. | | `page_size` | `Optional[int]` | No | Maximum number of tools to return per page | | `page_token` | `Optional[str]` | No | Token from a previous response for pagination | Returns `ListToolsResponse`: A page of tools, or of tool names with `summary`. **Node.js SDK:** `scalekit.actions.listTools` Lists the tools in your environment. Pass `connectionName` and `identifier`, or `connectedAccountId`, to include the custom MCP tools of that connected account. ```ts scalekit.actions.listTools( params?: { connectionName?: string; identifier?: string; provider?: string; toolName?: string[]; query?: string; organizationId?: string; userId?: string; connectedAccountId?: string; summary?: boolean; pageSize?: number; pageToken?: string; }, ): Promise ``` Example: ```ts const result = await scalekit.actions.listTools({ connectionName: "gmail", summary: true, pageSize: 5, }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connectionName` | `string` | No | The connection name, as shown in **AgentKit** > **Connections**. | | `identifier` | `string` | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `provider` | `string` | No | The app, such as `GMAIL`. | | `toolName` | `string[]` | No | Filter by one or more tool names | | `query` | `string` | No | Full-text search query to match tools by name or description (e.g., "gmail get attachment") | | `organizationId` | `string` | No | Organization ID to scope the connected account lookup | | `userId` | `string` | No | User ID to scope the connected account lookup | | `connectedAccountId` | `string` | No | Connected account ID. Alternative to filter.identifier + filter.connector for directly identifying the connected account whose custom MCP tools should be included. | | `summary` | `boolean` | No | Set to `true` to return only tool names, in `toolNames`. | | `pageSize` | `number` | No | Maximum number of tools to return per page | | `pageToken` | `string` | No | Token from a previous response for pagination | Returns `Promise`. **Used in** - [Build a Mastra agent with Scalekit AgentKit tools](https://docs.scalekit.com/cookbooks/mastra-agentkit/) - [Mint session tokens](https://docs.scalekit.com/agentkit/mcp/session-tokens/) ## List a user's scoped tools (beta) `GET /api/v1/tools/scoped` This endpoint is in beta. Its request and response may change. Tools already bound to one connected-account identifier. Use this when you need the list a user or agent is authorized to call (the list you pass to an LLM). `identifier` is required, and so is at least one `filter.*` field (provider, tool name or connection name): without one the API returns `INVALID_ARGUMENT`. **Query parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `identifier` | string | Yes | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `filter.connection_names` | array of string | No | Filter by one or more connection names. | | `filter.providers` | array of string | No | Filter by one or more tool providers. | | `filter.tool_names` | array of string | No | Filter by one or more tool names. | | `page_size` | integer | No | Maximum number of tools to return per page. | | `page_token` | string | No | Token from a previous response for pagination. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `next_page_token` | string | Token for fetching the next page of tools. | | `prev_page_token` | string | Token for fetching the previous page of tools. | | `tools` | array of object | List of tools scoped to the given connected account identifier. | | `tools.connected_account_id` | string | ID of the connected account for this scoped tool. | | `tools.identifier` | string | Your app's ID for the user, the value passed when the account was created. | | `tools.tool` | object | The underlying tool definition. | | `tools.tool.definition` | object | Tool definition in structured format. | | `tools.tool.id` | string | Unique ID of the tool. Immutable and read-only. | | `tools.tool.is_default` | boolean | Marks this tool as the default version for the combination. Read-only. | | `tools.tool.metadata` | object | Additional metadata about the tool. | | `tools.tool.provider` | string | Provider name (e.g. GOOGLE). Read-only. | | `tools.tool.tags` | array of string | Tags for categorization or filtering. | | `tools.tool.updated_at` | string | Timestamp when the tool was last updated. Read-only. | | `total_size` | integer | Total number of tools matching the query. | **Errors** - `400`: Invalid request - missing identifier or malformed filter/pagination parameters - `401`: Authentication required - missing or invalid access token **Request** ```bash curl -sS -G -X GET \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/tools/scoped" \ -H "Authorization: Bearer $TOKEN" \ --data-urlencode "identifier=user_123" \ --data-urlencode "filter.connection_names=gmail" \ --data-urlencode "page_size=2" ``` **Response** ```json { "tools": [ { "connected_account_id": "ca_24834495392086178", "identifier": "user_123", "tool": { "id": "tol_86147403291582051", "provider": "GMAIL", "definition": { "name": "gmail_fetch_mails", "description": "Fetch emails from a connected Gmail account using search filters. Requires a valid Gmail OAuth2 connection.", "schema_version": "1", "tool_version": "1", "input_schema": { "type": "object", "properties": { "query": { "type": [ "string", "null" ], "description": "Search query string using Gmail's search syntax (e.g., 'is:unread from:user@example.com')" }, "max_results": { "type": [ "integer", "null" ], "description": "Maximum number of emails to fetch" } } } }, "is_default": true, "metadata": {}, "tags": [] } }, { "connected_account_id": "ca_24834495392086178", "identifier": "user_123", "tool": { "id": "tol_86147403291583327", "provider": "GMAIL", "definition": { "name": "gmail_send_message", "description": "Send an email message immediately from the authenticated Gmail account. Constructs a MIME message and sends it via the Gmail API.", "schema_version": "1", "tool_version": "1", "input_schema": { "type": "object", "properties": { "to": { "type": "string", "description": "The recipient email address(es) for the message." }, "subject": { "type": "string", "description": "The subject line of the email." }, "body": { "type": "string", "description": "The body content of the email." } }, "required": [ "to", "subject", "body" ] } }, "is_default": true, "metadata": {}, "tags": [] } } ], "next_page_token": "eyJhZnRlciI6InRvbF84NjE0NzQwMzI5MTU4MzMyNyJ9", "prev_page_token": "", "total_size": 48 } ``` **Python SDK:** `scalekit_client.tools.list_scoped_tools` Lists the tools a user can call, narrowed by the filter. ```python scalekit_client.tools.list_scoped_tools( identifier: str, filter: Optional[ScopedToolFilter] = None, page_size: Optional[int] = None, page_token: Optional[str] = None, ) -> Tuple[ListScopedToolsResponse, grpc.Call] ``` Example: ```python page, _ = scalekit_client.tools.list_scoped_tools( identifier="user_123", filter={"connection_names": ["gmail"]}, page_size=2, ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `identifier` | `str` | Yes | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `filter` | `Optional[ScopedToolFilter]` | Yes | Filter parameters for scoped tools | | `page_size` | `Optional[int]` | No | Maximum number of tools to return per page | | `page_token` | `Optional[str]` | No | Token from a previous response for pagination | Returns `Tuple[ListScopedToolsResponse, grpc.Call]`: The response and the gRPC call. Unpack it as `page, _ = ...`. **Node.js SDK:** `scalekit.tools.listScopedTools` Lists tools that are scoped to a specific connected account identifier. ```ts scalekit.tools.listScopedTools( identifier: string, options: { filter: MessageInitShape; pageSize?: number; pageToken?: string; }, ): Promise ``` Example: ```ts const result = await scalekit.tools.listScopedTools("user_123", { filter: { connectionNames: ["gmail"] }, pageSize: 2, }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `identifier` | `string` | Yes | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `filter` | `MessageInitShape` | Yes | Filter configuration for scoped tools (providers, tool names, connection names). Required. | | `pageSize` | `number` | No | Maximum number of tools to return per page. | | `pageToken` | `string` | No | Token from a previous `listScopedTools` response for pagination. | Returns `Promise`. **Used in** - [Anthropic](https://docs.scalekit.com/agentkit/examples/anthropic/) - [Call your connector](https://docs.scalekit.com/agentkit/bring-your-own-connector/making-tool-calls/) - [FastRouter + Scalekit tool calling](https://docs.scalekit.com/cookbooks/fastrouter-agentkit-tool-calling/) - [LangGraph](https://docs.scalekit.com/agentkit/examples/langgraph/) - [Limit an agent to read-only tools](https://docs.scalekit.com/agentkit/mcp/read-only-tokens/) - [Migrate from Composio to Scalekit](https://docs.scalekit.com/agentkit/advanced/migrate-from-composio/) - [OpenAI](https://docs.scalekit.com/agentkit/examples/openai/) - [OpenAI Agents SDK](https://docs.scalekit.com/agentkit/examples/openai-agents-sdk/) - [Use built-in tools](https://docs.scalekit.com/agentkit/tools/scalekit-optimized-tools/) - [Vercel AI SDK](https://docs.scalekit.com/agentkit/examples/vercel-ai/) ## List tools available to a user (beta) `GET /api/v1/tools/available` This endpoint is in beta. Its request and response may change. Lists every tool that a user's connected accounts can call, across all their connections. `identifier` is required. To narrow the list to chosen connections, providers or tools, use [List a user's scoped tools](https://docs.scalekit.com/agentkit/reference/tools/list-scoped-tools/). **Query parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `identifier` | string | Yes | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `page_size` | integer | No | Maximum number of tools to return per page. | | `page_token` | string | No | Token from a previous response for pagination. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `next_page_token` | string | Token for fetching the next page of tools. | | `prev_page_token` | string | Token for fetching the previous page of tools. | | `tools` | array of object | List of tools available for the identifier. | | `tools.definition` | object | Tool definition in structured format. | | `tools.id` | string | Unique ID of the tool. Immutable and read-only. | | `tools.is_default` | boolean | Marks this tool as the default version for the combination. Read-only. | | `tools.metadata` | object | Additional metadata about the tool. | | `tools.provider` | string | Provider name (e.g. GOOGLE). Read-only. | | `tools.tags` | array of string | Tags for categorization or filtering. | | `tools.updated_at` | string | Timestamp when the tool was last updated. Read-only. | | `total_size` | integer | Total number of available tools matching the query. | **Errors** - `400`: Invalid request - missing or malformed identifier - `401`: Authentication required - missing or invalid access token - `404`: Identifier not found **Request** ```bash curl -sS -G -X GET \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/tools/available" \ -H "Authorization: Bearer $TOKEN" \ --data-urlencode "identifier=user_123" \ --data-urlencode "page_size=2" ``` **Response** ```json { "tools": [ { "id": "tol_86147403291582051", "provider": "GMAIL", "definition": { "name": "gmail_fetch_mails", "description": "Fetch emails from a connected Gmail account using search filters. Requires a valid Gmail OAuth2 connection.", "schema_version": "1", "tool_version": "1", "input_schema": { "type": "object", "properties": { "query": { "type": [ "string", "null" ], "description": "Search query string using Gmail's search syntax (e.g., 'is:unread from:user@example.com')" }, "max_results": { "type": [ "integer", "null" ], "description": "Maximum number of emails to fetch" } } } }, "is_default": true, "metadata": {}, "tags": [] }, { "id": "tol_86147403291583327", "provider": "GMAIL", "definition": { "name": "gmail_send_message", "description": "Send an email message immediately from the authenticated Gmail account. Constructs a MIME message and sends it via the Gmail API.", "schema_version": "1", "tool_version": "1", "input_schema": { "type": "object", "properties": { "to": { "type": "string", "description": "The recipient email address(es) for the message." }, "subject": { "type": "string", "description": "The subject line of the email." }, "body": { "type": "string", "description": "The body content of the email." } }, "required": [ "to", "subject", "body" ] } }, "is_default": true, "metadata": {}, "tags": [] } ], "next_page_token": "eyJhZnRlciI6InRvbF84NjE0NzQwMzI5MTU4MzMyNyJ9", "prev_page_token": "", "total_size": 48 } ``` **Python:** Call this endpoint over REST, as the example shows. ```python import os import requests env_url = os.environ["SCALEKIT_ENVIRONMENT_URL"] token = requests.post( f"{env_url}/oauth/token", data={ "grant_type": "client_credentials", "client_id": os.environ["SCALEKIT_CLIENT_ID"], "client_secret": os.environ["SCALEKIT_CLIENT_SECRET"], }, ).json()["access_token"] response = requests.get( f"{env_url}/api/v1/tools/available", headers={"Authorization": f"Bearer {token}"}, params={"identifier": "user_123", "page_size": 2}, ) response.raise_for_status() result = response.json() ``` **Node.js SDK:** `scalekit.tools.listAvailableTools` Lists tools that are available for a specific connected account identifier. Returns every tool the user's connected accounts can call, not only the ones you pass to your agent. ```ts scalekit.tools.listAvailableTools( identifier: string, options?: { pageSize?: number; pageToken?: string; }, ): Promise ``` Example: ```ts const result = await scalekit.tools.listAvailableTools("user_123", { pageSize: 2, }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `identifier` | `string` | Yes | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `pageSize` | `number` | No | Maximum number of tools to return per page. | | `pageToken` | `string` | No | Token from a previous `listAvailableTools` response for pagination. | Returns `Promise`. ## Search tools (beta) `POST /api/v1/tools:search` This endpoint is in beta. Its request and response may change. Ranks the tools in your environment against a plain-language query, such as `send an email`, and returns the best matches from every connection. Pass `identifier` to see, for each result, whether that user can call it now: each entry in `connections` has a `readiness_state` of `TOOL_READINESS_STATE_READY`, `TOOL_READINESS_STATE_NEEDS_CONNECTION` (the account exists but isn't active) or `TOOL_READINESS_STATE_NEEDS_REAUTH`. Check it before you [execute the tool](https://docs.scalekit.com/agentkit/reference/tools/execute-a-tool/). An empty `connections` list means the user has no account on any connection for that tool's provider; it isn't an error. **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | Yes | Natural-language query or keywords describing the job to be done. Ranked against tool names, descriptions, and providers. 1-256 characters. | | `identifier` | string | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `top_k` | integer | No | Maximum number of ranked results to return. Defaults to 10, capped at 50. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `tools` | array of object | Tools matching the query, ordered by descending relevance score. | | `tools.connections` | array of object | The connections for this tool's provider where the user has a connected account, each with its own `readiness_state`. Returned only when the request has `identifier`. Empty when the user has no account on any of them. More than one entry means the user has accounts on several connections, such as two Slack workspaces. | | `tools.connections.connected_account_id` | string | The user's connected account on this connection, whatever its readiness. Pass it to Execute a tool only when `readiness_state` is `TOOL_READINESS_STATE_READY`. | | `tools.connections.connection_name` | string | Name of this connection. | | `tools.connections.readiness_state` | string (enum) | Whether this specific connection is usable right now for the supplied identifier, independent of every other connection listed for this provider. One of: `TOOL_READINESS_STATE_READY`, `TOOL_READINESS_STATE_NEEDS_CONNECTION`, `TOOL_READINESS_STATE_NEEDS_REAUTH`. | | `tools.description` | string | Human-readable description of what the tool does. | | `tools.name` | string | The tool's name, to pass as `tool_name` when you [execute it](https://docs.scalekit.com/agentkit/reference/tools/execute-a-tool/). | | `tools.provider` | string | Provider the tool belongs to. | | `tools.score` | number | Relevance score for this result. Higher is better; comparable only within a single response. | **Errors** - `400`: Invalid request - the query is empty or exceeds the maximum length - `401`: Authentication required - missing or invalid access token **Request** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/tools:search" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "query": "send an email", "identifier": "user_123", "top_k": 5 }' ``` **Response** ```json { "tools": [ { "name": "gmail_send_message", "description": "Send an email message immediately from the authenticated Gmail account. Constructs a MIME message and sends it via the Gmail API.", "provider": "GMAIL", "score": 0.87, "connections": [ { "connection_name": "gmail", "connected_account_id": "ca_24834495392086178", "readiness_state": "TOOL_READINESS_STATE_READY" } ] }, { "name": "slack_send_message", "description": "Send plain text to a Slack channel or DM, optionally in a thread. Returns channel and message timestamp.", "provider": "SLACK", "score": 0.61, "connections": [] } ] } ``` **Python SDK:** `scalekit_client.tools.search_tools` Ranks the tools a user can call by how well they match a plain-language query. Each result's `score` is a relevance score where higher is better; it is only comparable within the results of a single response, not across separate calls. `connections` on each result is populated only when `identifier` is set: an empty list means the identifier has no connection at all for that tool's provider (not an error, and different from `NEEDS_CONNECTION`, which means a connected account row exists but is inactive); more than one entry means the identifier has accounts on multiple connections for that provider (for example, two Slack workspaces) -- inspect each entry's own `readiness_state` rather than assuming one answer for the whole tool. ```python scalekit_client.tools.search_tools( query: str, identifier: Optional[str] = None, top_k: Optional[int] = None, ) -> Tuple[SearchToolsResponse, grpc.Call] ``` Example: ```python results, _ = scalekit_client.tools.search_tools( query="send an email", identifier="user_123", top_k=5, ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | `str` | Yes | Natural-language query or keywords describing the job to be done | | `identifier` | `Optional[str]` | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `top_k` | `Optional[int]` | No | Maximum number of ranked results to return (default 10, capped at 50) | Returns `Tuple[SearchToolsResponse, grpc.Call]`: The response and the gRPC call. Unpack it as `results, _ = ...`. **Node.js SDK:** `scalekit.tools.searchTools` Searches tools ranked by relevance to a natural-language query — the job to be done, not an exact tool name. Each result's `score` is a relevance score where higher is better; it is only comparable within the results of a single response, not across separate calls. Pass `identifier` to also get per-connection readiness (usable now, needs a new connection, or needs re-auth) on each result's `connections`. `connections` is populated only when `identifier` is set: an empty array means the identifier has no connection at all for that tool's provider (not an error, and different from `NEEDS_CONNECTION`, which means a connected-account row exists but is inactive); more than one entry means the identifier has accounts on multiple connections for that provider (for example, two Slack workspaces) — inspect each entry's own `readinessState` rather than assuming one answer for the whole tool. ```ts scalekit.tools.searchTools( query: string, options?: { identifier?: string; topK?: number; }, ): Promise ``` Example: ```ts const result = await scalekit.tools.searchTools("send an email", { identifier: "user_123", topK: 5, }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | `string` | Yes | Natural-language query or keywords describing the job to be done. 1-256 characters. | | `identifier` | `string` | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `topK` | `number` | No | Maximum number of ranked results to return. Defaults to 10, capped at 50. | Returns `Promise`. **Used in** - [Use built-in tools](https://docs.scalekit.com/agentkit/tools/scalekit-optimized-tools/) --- Source: https://docs.scalekit.com/agentkit/reference/virtual-mcp-servers.md # Virtual MCP servers A Virtual MCP server exposes the tools you choose to an MCP client, for one user at a time. In the API a server is an MCP configuration, under `/api/v1/mcp/configs`. See [Create a Virtual MCP server](https://docs.scalekit.com/agentkit/mcp/configure-mcp-server/) and [Mint session tokens](https://docs.scalekit.com/agentkit/mcp/session-tokens/). A server exposes its tools in one of two ways, set with `tool_exposure`. `DIRECT`, the default, lists every tool you map to the server, for up to 25 connections. `META` gives the agent five tools to search for, inspect and run the mapped tools, and to connect the user's accounts, with no connection limit. See [How an agent uses a META server](https://docs.scalekit.com/agentkit/mcp/configure-mcp-server/#how-an-agent-uses-a-meta-server). One page per endpoint, each with its own markdown copy: | Endpoint | Request | What it does | Python SDK | Node.js SDK | | --- | --- | --- | --- | --- | | [Create a Virtual MCP server](https://docs.scalekit.com/agentkit/reference/virtual-mcp-servers/create-a-virtual-mcp-server.md) | `POST /api/v1/mcp/configs` | Create a server that serves the connections and tools you choose. | `actions.mcp.create_config` | `actions.mcp.createConfig` | | [Get a Virtual MCP server](https://docs.scalekit.com/agentkit/reference/virtual-mcp-servers/get-a-virtual-mcp-server.md) | `GET /api/v1/mcp/configs/{config_id}` | Get a server's connections, tools and MCP server URL. | REST only | `actions.mcp.getConfig` | | [List Virtual MCP servers](https://docs.scalekit.com/agentkit/reference/virtual-mcp-servers/list-virtual-mcp-servers.md) | `GET /api/v1/mcp/configs` | List the servers in your environment, with a search on their name. | `actions.mcp.list_configs` | `actions.mcp.listConfigs` | | [Update a Virtual MCP server](https://docs.scalekit.com/agentkit/reference/virtual-mcp-servers/update-a-virtual-mcp-server.md) | `PUT /api/v1/mcp/configs/{config_id}` | Change a server's description, the connections and tools it serves, or how it exposes tools. | `actions.mcp.update_config` | `actions.mcp.updateConfig` | | [Delete a Virtual MCP server](https://docs.scalekit.com/agentkit/reference/virtual-mcp-servers/delete-a-virtual-mcp-server.md) | `DELETE /api/v1/mcp/configs/{config_id}` | Delete a server and the connections and tools it serves. | `actions.mcp.delete_config` | `actions.mcp.deleteConfig` | | [Check a user's connected accounts for a server](https://docs.scalekit.com/agentkit/reference/virtual-mcp-servers/check-connected-accounts-for-a-server.md) | `POST /api/v1/mcp/configs/{config_id}/connected_accounts` | Check a user's accounts for a server, with links for any that aren't active. | `actions.mcp.list_mcp_connected_accounts` | `actions.mcp.listConnectedAccounts` | | [Mint a session token](https://docs.scalekit.com/agentkit/reference/virtual-mcp-servers/mint-a-session-token.md) | `POST /api/v1/mcp/configs/{mcp_config_id}/tokens` | Mint a short-lived token that lets a user's MCP client call the server. | `actions.mcp.create_session_token` | `actions.mcp.createSessionToken` | | [Mint a session token for a connection](https://docs.scalekit.com/agentkit/reference/virtual-mcp-servers/mint-a-connection-session-token.md) | `POST /api/v1/mcp/connections/{key_id}/tokens` | Mint a short-lived token that lets a user's MCP client call one connection's tools. | `actions.mcp.create_session_token` | `actions.mcp.createSessionToken` | ## Create a Virtual MCP server `POST /api/v1/mcp/configs` Creates a new MCP configuration with a set of connections and tools. **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | Yes | Unique name for the MCP configuration. Must be 1–100 characters. Allowed characters: lowercase letters (a–z), digits (0–9), hyphens (-), and underscores (_). | | `connection_tool_mappings` | array of object | No | List of connection-to-tool mappings for this MCP config. Maximum 25 connections in `DIRECT` mode. | | `connection_tool_mappings.connection_name` | string | Yes | Developer-assigned connection name. | | `connection_tool_mappings.tools` | array of string | No | List of tool names linked to this connection (empty = all tools). | | `description` | string | No | Description of the MCP configuration. | | `tool_exposure` | string (enum) | No | How the server exposes tools to agents. `DIRECT` lists every tool you map to it, for up to 25 connections. `META` gives agents five tools to search for, inspect and run the mapped tools, with no limit on connections. `DIRECT` is the default when you create a server without it. One of: `DIRECT`, `META`. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `config` | object | The created MCP configuration. | | `config.connection_tool_mappings` | array of object | List of connection-to-tool mappings for this MCP config. Maximum 25 connections in `DIRECT` mode. | | `config.connection_tool_mappings.connected_account_id` | string | Connected account backing this connection in the MCP instance context. | | `config.connection_tool_mappings.connected_account_status` | string | Authentication status for the connected account. | | `config.connection_tool_mappings.connection_id` | string | Unique ID of the connection. | | `config.connection_tool_mappings.connection_name` | string | Developer-assigned connection name. | | `config.connection_tool_mappings.provider` | string | Provider name for this connection. | | `config.connection_tool_mappings.tools` | array of string | List of tool names linked to this connection (empty = all tools). | | `config.description` | string | Description of the MCP configuration. | | `config.id` | string | Unique ID of the MCP config. | | `config.mcp_server_url` | string | The URL MCP clients connect to for this server. Every user and session shares it; the session token identifies the user. | | `config.name` | string | Unique name for the MCP configuration. Must be 1–100 characters. Allowed characters: lowercase letters (a–z), digits (0–9), hyphens (-), and underscores (_). | | `config.tool_exposure` | string (enum) | How the server exposes tools to agents. `DIRECT` lists every tool you map to it, for up to 25 connections. `META` gives agents five tools to search for, inspect and run the mapped tools, with no limit on connections. `DIRECT` is the default when you create a server without it. One of: `DIRECT`, `META`. | **Errors** - `400`: Invalid request - missing required fields or invalid connection/tool mappings - `401`: Authentication required - missing or invalid access token **Request** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/mcp/configs" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "repo-assistant", "description": "Reads the user'\''s GitHub repositories and issues", "connection_tool_mappings": [ { "connection_name": "github-connect", "tools": [ "github_user_repos_list", "github_issues_list" ] } ], "tool_exposure": "DIRECT" }' ``` **Response** ```json { "config": { "id": "cfg_85630864460904897", "name": "repo-assistant", "description": "Reads the user's GitHub repositories and issues", "connection_tool_mappings": [ { "connection_id": "conn_70219645518267719", "connection_name": "github-connect", "provider": "GITHUB", "tools": [ "github_user_repos_list", "github_issues_list" ] } ], "mcp_server_url": "https://your-env.scalekit.dev/mcp/v3/servers/3d9f6a2e-7b41-4c8e-a5d2-0f1e8b6c4a93", "tool_exposure": "DIRECT" } } ``` **Python SDK:** `scalekit_client.actions.mcp.create_config` Create a new MCP configuration from the supplied metadata and tool mappings. ```python scalekit_client.actions.mcp.create_config( name: str, description: Optional[str] = None, connection_tool_mappings: Optional[List[McpConfigConnectionToolMapping]] = None, tool_exposure: Optional[str] = None, ) -> CreateMcpConfigResponse ``` Example: ```python from scalekit.actions.types import McpConfigConnectionToolMapping result = scalekit_client.actions.mcp.create_config( name="repo-assistant", description="Reads the user's GitHub repositories and issues", connection_tool_mappings=[ McpConfigConnectionToolMapping( connection_name="github-connect", tools=["github_user_repos_list", "github_issues_list"], ), ], tool_exposure="DIRECT", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `name` | `str` | Yes | Human readable name for the configuration. | | `description` | `Optional[str]` | No | Summary that surfaces in dashboards and APIs. | | `connection_tool_mappings` | `Optional[List[McpConfigConnectionToolMapping]]` | No | Explicit mapping between connectors and tools. | | `tool_exposure` | `Optional[str]` | No | How the server exposes tools to agents. `DIRECT` lists every tool you map to the server, for up to 25 connections. `META` gives agents five tools, `search_available_tools`, `list_available_tools`, `get_tool_details`, `execute_tool` and `connect_account`, to find and run the mapped tools, with no connection limit. Defaults to `DIRECT`. | Returns `CreateMcpConfigResponse`: The created configuration payload. **Node.js SDK:** `scalekit.actions.mcp.createConfig` Creates a Virtual MCP server configuration. Create this once per agent role. The response carries a static `config.mcpServerUrl` that every user and every session reuses. ```ts scalekit.actions.mcp.createConfig( params: { name: string; description?: string; connectionToolMappings?: MessageInitShape['connectionToolMappings']; toolExposure?: McpToolExposure; }, ): Promise ``` Example: ```ts const result = await scalekit.actions.mcp.createConfig({ name: "repo-assistant", description: "Reads the user's GitHub repositories and issues", connectionToolMappings: [ { connectionName: "github-connect", tools: ["github_user_repos_list", "github_issues_list"], }, ], toolExposure: "DIRECT", }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `name` | `string` | Yes | Unique name. 1-100 characters, lowercase letters, digits, hyphens and underscores. | | `description` | `string` | No | Human-readable summary of what this server exposes. | | `connectionToolMappings` | `MessageInitShape['connectionToolMappings']` | No | Which connections, and which tools from each, to expose. Omit `tools` on a mapping to expose every tool for that connection. Maximum 25 connections when `toolExposure` is `'DIRECT'`. | | `toolExposure` | `McpToolExposure` | No | How the server exposes tools to agents. `DIRECT` lists every tool you map to the server, for up to 25 connections. `META` gives agents five tools, `search_available_tools`, `list_available_tools`, `get_tool_details`, `execute_tool` and `connect_account`, to find and run the mapped tools, with no connection limit. Defaults to `DIRECT`. | Returns `Promise`. **Used in** - [Claude Managed Agents](https://docs.scalekit.com/agentkit/examples/claude-managed-agents/) - [Create a Virtual MCP server](https://docs.scalekit.com/agentkit/mcp/configure-mcp-server/) - [Mastra](https://docs.scalekit.com/agentkit/examples/mastra/) ## Get a Virtual MCP server `GET /api/v1/mcp/configs/{config_id}` Returns a single MCP configuration for the current environment by ID. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `config_id` | string | Yes | ID of the MCP configuration to fetch. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `config` | object | The requested MCP configuration. | | `config.connection_tool_mappings` | array of object | List of connection-to-tool mappings for this MCP config. Maximum 25 connections in `DIRECT` mode. | | `config.connection_tool_mappings.connected_account_id` | string | Connected account backing this connection in the MCP instance context. | | `config.connection_tool_mappings.connected_account_status` | string | Authentication status for the connected account. | | `config.connection_tool_mappings.connection_id` | string | Unique ID of the connection. | | `config.connection_tool_mappings.connection_name` | string | Developer-assigned connection name. | | `config.connection_tool_mappings.provider` | string | Provider name for this connection. | | `config.connection_tool_mappings.tools` | array of string | List of tool names linked to this connection (empty = all tools). | | `config.description` | string | Description of the MCP configuration. | | `config.id` | string | Unique ID of the MCP config. | | `config.mcp_server_url` | string | The URL MCP clients connect to for this server. Every user and session shares it; the session token identifies the user. | | `config.name` | string | Unique name for the MCP configuration. Must be 1–100 characters. Allowed characters: lowercase letters (a–z), digits (0–9), hyphens (-), and underscores (_). | | `config.tool_exposure` | string (enum) | How the server exposes tools to agents. `DIRECT` lists every tool you map to it, for up to 25 connections. `META` gives agents five tools to search for, inspect and run the mapped tools, with no limit on connections. `DIRECT` is the default when you create a server without it. One of: `DIRECT`, `META`. | **Errors** - `400`: Invalid request - `401`: Authentication required - `404`: Not Found - MCP configuration does not exist **Request** ```bash curl -sS -X GET \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/mcp/configs/cfg_85630864460904897" \ -H "Authorization: Bearer $TOKEN" ``` **Response** ```json { "config": { "id": "cfg_85630864460904897", "name": "repo-assistant", "description": "Reads the user's GitHub repositories and issues", "connection_tool_mappings": [ { "connection_id": "conn_70219645518267719", "connection_name": "github-connect", "provider": "GITHUB", "tools": [ "github_user_repos_list", "github_issues_list" ] } ], "mcp_server_url": "https://your-env.scalekit.dev/mcp/v3/servers/3d9f6a2e-7b41-4c8e-a5d2-0f1e8b6c4a93", "tool_exposure": "DIRECT" } } ``` **Python:** Call this endpoint over REST, as the example shows. ```python import os import requests env_url = os.environ["SCALEKIT_ENVIRONMENT_URL"] token = requests.post( f"{env_url}/oauth/token", data={ "grant_type": "client_credentials", "client_id": os.environ["SCALEKIT_CLIENT_ID"], "client_secret": os.environ["SCALEKIT_CLIENT_SECRET"], }, ).json()["access_token"] response = requests.get( f"{env_url}/api/v1/mcp/configs/cfg_85630864460904897", headers={"Authorization": f"Bearer {token}"}, ) response.raise_for_status() result = response.json() ``` **Node.js SDK:** `scalekit.actions.mcp.getConfig` Fetches a single MCP configuration by ID. ```ts scalekit.actions.mcp.getConfig( configId: string, ): Promise ``` Example: ```ts const result = await scalekit.actions.mcp.getConfig("cfg_85630864460904897"); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `configId` | `string` | Yes | ID of the configuration to fetch (`cfg_...`). | Returns `Promise`. ## List Virtual MCP servers `GET /api/v1/mcp/configs` Lists the Virtual MCP servers in your environment. Filter by ID, exact name, provider or MCP server URL, or pass `search` to match part of the name. **Query parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `filter.id` | string | No | Filter by MCP configuration id. | | `filter.mcp_server_url` | string | No | Filter configs by MCP server URL. The UUID is extracted from the last path segment of the URL and used to find the matching configuration. | | `filter.name` | string | No | Case-insensitive exact match on configuration name. Allowed characters: letters (a–z, A–Z), digits (0–9), hyphens (-), and underscores (_). Maximum 100 characters. | | `filter.provider` | string | No | Return only servers with a connection for this app, such as `GMAIL`. | | `page_size` | integer | No | Number of configs to return per page (max 30). | | `page_token` | string | No | Pagination token to fetch the next or previous page. | | `search` | string | No | Return servers whose name contains this text, case-insensitive. At least 3 characters. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `configs` | array of object | List of MCP configurations. | | `configs.connection_tool_mappings` | array of object | List of connection-to-tool mappings for this MCP config. Maximum 25 connections in `DIRECT` mode. | | `configs.connection_tool_mappings.connected_account_id` | string | Connected account backing this connection in the MCP instance context. | | `configs.connection_tool_mappings.connected_account_status` | string | Authentication status for the connected account. | | `configs.connection_tool_mappings.connection_id` | string | Unique ID of the connection. | | `configs.connection_tool_mappings.connection_name` | string | Developer-assigned connection name. | | `configs.connection_tool_mappings.provider` | string | Provider name for this connection. | | `configs.connection_tool_mappings.tools` | array of string | List of tool names linked to this connection (empty = all tools). | | `configs.description` | string | Description of the MCP configuration. | | `configs.id` | string | Unique ID of the MCP config. | | `configs.mcp_server_url` | string | The URL MCP clients connect to for this server. Every user and session shares it; the session token identifies the user. | | `configs.name` | string | Unique name for the MCP configuration. Must be 1–100 characters. Allowed characters: lowercase letters (a–z), digits (0–9), hyphens (-), and underscores (_). | | `configs.tool_exposure` | string (enum) | How the server exposes tools to agents. `DIRECT` lists every tool you map to it, for up to 25 connections. `META` gives agents five tools to search for, inspect and run the mapped tools, with no limit on connections. `DIRECT` is the default when you create a server without it. One of: `DIRECT`, `META`. | | `next_page_token` | string | Pagination token to fetch the next page. | | `prev_page_token` | string | Pagination token to fetch the previous page. | | `total_size` | integer | Total number of configs matching the filter. | **Errors** - `400`: Invalid request - bad filter or pagination parameters - `401`: Authentication required **Request** ```bash curl -sS -G -X GET \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/mcp/configs" \ -H "Authorization: Bearer $TOKEN" \ --data-urlencode "search=repo-assistant" ``` **Response** ```json { "configs": [ { "id": "cfg_85630864460904897", "name": "repo-assistant", "description": "Reads the user's GitHub repositories and issues", "connection_tool_mappings": [ { "connection_id": "conn_70219645518267719", "connection_name": "github-connect", "provider": "GITHUB", "tools": [ "github_user_repos_list", "github_issues_list" ] } ], "mcp_server_url": "https://your-env.scalekit.dev/mcp/v3/servers/3d9f6a2e-7b41-4c8e-a5d2-0f1e8b6c4a93", "tool_exposure": "DIRECT" } ], "next_page_token": "", "prev_page_token": "", "total_size": 1 } ``` **Python SDK:** `scalekit_client.actions.mcp.list_configs` List MCP configurations with optional pagination and filtering. ```python scalekit_client.actions.mcp.list_configs( page_size: Optional[int] = None, page_token: Optional[str] = None, filter_id: Optional[str] = None, filter_provider: Optional[str] = None, filter_name: Optional[str] = None, filter_mcp_server_url: Optional[str] = None, search: Optional[str] = None, ) -> ListMcpConfigsResponse ``` Example: ```python result = scalekit_client.actions.mcp.list_configs( search="repo-assistant", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `page_size` | `Optional[int]` | No | Maximum number of configs to include in the current page. Defaults to the server-side default (typically 20). | | `page_token` | `Optional[str]` | No | Cursor token returned by a previous `list_configs` call. Pass this to fetch the next page of results. | | `filter_id` | `Optional[str]` | No | Return only the server with this ID, such as `"cfg_85630864460904897"`. | | `filter_provider` | `Optional[str]` | No | Return only servers with a connection for this app, such as `"GMAIL"`. | | `filter_name` | `Optional[str]` | No | Return only the server with this name, case-insensitive exact match, such as `"repo-assistant"`. | | `filter_mcp_server_url` | `Optional[str]` | No | Return only the server with this MCP server URL, such as `"https://your-env.scalekit.dev/mcp/v3/servers/3d9f6a2e-7b41-4c8e-a5d2-0f1e8b6c4a93"`. | | `search` | `Optional[str]` | No | Return servers whose name contains this text, case-insensitive. At least 3 characters. | Returns `ListMcpConfigsResponse`: The page of servers in `configs`, with `next_page_token` and `total_size`. **Node.js SDK:** `scalekit.actions.mcp.listConfigs` Lists MCP configurations for the current environment. ```ts scalekit.actions.mcp.listConfigs( options?: { search?: string; pageSize?: number; pageToken?: string; }, ): Promise ``` Example: ```ts const result = await scalekit.actions.mcp.listConfigs({ search: "repo-assistant", }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `search` | `string` | No | Return servers whose name contains this text, case-insensitive. At least 3 characters. | | `pageSize` | `number` | No | Maximum number of configurations to return per page (max 30; a larger value fails with `[invalid_argument] Validation error`). | | `pageToken` | `string` | No | Token from a previous `listConfigs` response. | Returns `Promise`. **Used in** - [Build a multi-agent email triage crew with CrewAI](https://docs.scalekit.com/cookbooks/crewai-agentkit-email-triage/) - [Claude Managed Agents](https://docs.scalekit.com/agentkit/examples/claude-managed-agents/) - [Create a Virtual MCP server](https://docs.scalekit.com/agentkit/mcp/configure-mcp-server/) - [CrewAI](https://docs.scalekit.com/agentkit/examples/crewai/) - [Mastra](https://docs.scalekit.com/agentkit/examples/mastra/) ## Update a Virtual MCP server `PUT /api/v1/mcp/configs/{config_id}` Updates the description and connection-to-tool mappings for an existing MCP configuration. The configuration name cannot be changed after creation. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `config_id` | string | Yes | ID of the MCP configuration to update. | **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connection_tool_mappings` | array of object | No | Updated list of connection-to-tool mappings for this MCP config. Maximum 25 connections in `DIRECT` mode. | | `connection_tool_mappings.connection_name` | string | Yes | Developer-assigned connection name. | | `connection_tool_mappings.tools` | array of string | No | List of tool names linked to this connection (empty = all tools). | | `description` | string | No | Updated description for the MCP configuration. | | `tool_exposure` | string (enum) | No | Change how the server exposes tools to agents: `DIRECT` or `META`. Leave it out to keep the current mode. Switching to `DIRECT` fails when the server has more than 25 connections. After a switch, MCP clients must list the tools again; session tokens keep working. One of: `DIRECT`, `META`. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `config` | object | The updated MCP configuration. | | `config.connection_tool_mappings` | array of object | List of connection-to-tool mappings for this MCP config. Maximum 25 connections in `DIRECT` mode. | | `config.connection_tool_mappings.connected_account_id` | string | Connected account backing this connection in the MCP instance context. | | `config.connection_tool_mappings.connected_account_status` | string | Authentication status for the connected account. | | `config.connection_tool_mappings.connection_id` | string | Unique ID of the connection. | | `config.connection_tool_mappings.connection_name` | string | Developer-assigned connection name. | | `config.connection_tool_mappings.provider` | string | Provider name for this connection. | | `config.connection_tool_mappings.tools` | array of string | List of tool names linked to this connection (empty = all tools). | | `config.description` | string | Description of the MCP configuration. | | `config.id` | string | Unique ID of the MCP config. | | `config.mcp_server_url` | string | The URL MCP clients connect to for this server. Every user and session shares it; the session token identifies the user. | | `config.name` | string | Unique name for the MCP configuration. Must be 1–100 characters. Allowed characters: lowercase letters (a–z), digits (0–9), hyphens (-), and underscores (_). | | `config.tool_exposure` | string (enum) | How the server exposes tools to agents. `DIRECT` lists every tool you map to it, for up to 25 connections. `META` gives agents five tools to search for, inspect and run the mapped tools, with no limit on connections. `DIRECT` is the default when you create a server without it. One of: `DIRECT`, `META`. | **Errors** - `400`: Invalid request - malformed payload or invalid mappings - `401`: Authentication required - `404`: Not Found - MCP configuration does not exist **Request** ```bash curl -sS -X PUT \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/mcp/configs/cfg_85630864460904897" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "description": "Reads the user'\''s GitHub repositories, issues and pull requests", "connection_tool_mappings": [ { "connection_name": "github-connect", "tools": [ "github_user_repos_list", "github_issues_list", "github_pull_requests_list" ] } ] }' ``` **Response** ```json { "config": { "id": "cfg_85630864460904897", "name": "repo-assistant", "description": "Reads the user's GitHub repositories, issues and pull requests", "connection_tool_mappings": [ { "connection_id": "conn_70219645518267719", "connection_name": "github-connect", "provider": "GITHUB", "tools": [ "github_user_repos_list", "github_issues_list", "github_pull_requests_list" ] } ], "mcp_server_url": "https://your-env.scalekit.dev/mcp/v3/servers/3d9f6a2e-7b41-4c8e-a5d2-0f1e8b6c4a93", "tool_exposure": "DIRECT" } } ``` **Python SDK:** `scalekit_client.actions.mcp.update_config` Update mutable fields on an existing MCP configuration. ```python scalekit_client.actions.mcp.update_config( config_id: str, description: Optional[str] = None, connection_tool_mappings: Optional[List[McpConfigConnectionToolMapping]] = None, tool_exposure: Optional[str] = None, ) -> UpdateMcpConfigResponse ``` Example: ```python from scalekit.actions.types import McpConfigConnectionToolMapping result = scalekit_client.actions.mcp.update_config( config_id="cfg_85630864460904897", description="Reads the user's GitHub repositories, issues and pull requests", connection_tool_mappings=[ McpConfigConnectionToolMapping( connection_name="github-connect", tools=[ "github_user_repos_list", "github_issues_list", "github_pull_requests_list", ], ), ], ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `config_id` | `str` | Yes | Identifier of the configuration to update. | | `description` | `Optional[str]` | No | New description to persist, if provided. | | `connection_tool_mappings` | `Optional[List[McpConfigConnectionToolMapping]]` | No | Replacement connector-to-tool mappings. | | `tool_exposure` | `Optional[str]` | No | Change how the server exposes tools: `DIRECT` or `META`. Leave it out to keep the current mode. Switching to `DIRECT` fails when the server has more than 25 connections or two connections map tools with the same name. After a switch, MCP clients must list the tools again; session tokens keep working. | Returns `UpdateMcpConfigResponse`: The updated configuration payload. **Node.js SDK:** `scalekit.actions.mcp.updateConfig` Updates the description and connection-to-tool mappings of a configuration. The name cannot be changed after creation. Avoid updating while agent sessions are running — tools can become unavailable mid-session. For a significant change, create a new configuration and swap the URL instead. ```ts scalekit.actions.mcp.updateConfig( params: { configId: string; description?: string; connectionToolMappings?: MessageInitShape['connectionToolMappings']; toolExposure?: McpToolExposure; }, ): Promise ``` Example: ```ts const result = await scalekit.actions.mcp.updateConfig({ configId: "cfg_85630864460904897", description: "Reads the user's GitHub repositories, issues and pull requests", connectionToolMappings: [ { connectionName: "github-connect", tools: [ "github_user_repos_list", "github_issues_list", "github_pull_requests_list", ], }, ], }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `configId` | `string` | Yes | ID of the configuration to update. | | `description` | `string` | No | New description. | | `connectionToolMappings` | `MessageInitShape['connectionToolMappings']` | No | Replacement connection-to-tool mappings. | | `toolExposure` | `McpToolExposure` | No | Change how the server exposes tools: `DIRECT` or `META`. Leave it out to keep the current mode. Switching to `DIRECT` fails when the server has more than 25 connections or two connections map tools with the same name. After a switch, MCP clients must list the tools again; session tokens keep working. | Returns `Promise`. **Used in** - [Create a Virtual MCP server](https://docs.scalekit.com/agentkit/mcp/configure-mcp-server/) ## Delete a Virtual MCP server `DELETE /api/v1/mcp/configs/{config_id}` Deletes a Virtual MCP server and its connection and tool mappings. MCP clients can no longer connect to its URL. Users keep their connected accounts. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `config_id` | string | Yes | ID of the MCP configuration to delete. | **Errors** - `400`: Invalid request - `401`: Authentication required - `404`: Not Found - MCP configuration does not exist **Request** ```bash curl -sS -X DELETE \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/mcp/configs/cfg_85630864460904897" \ -H "Authorization: Bearer $TOKEN" ``` **Python SDK:** `scalekit_client.actions.mcp.delete_config` Deletes a Virtual MCP server. ```python scalekit_client.actions.mcp.delete_config( config_id: str, ) -> DeleteMcpConfigResponse ``` Example: ```python result = scalekit_client.actions.mcp.delete_config( config_id="cfg_85630864460904897", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `config_id` | `str` | Yes | Identifier of the configuration to remove. | Returns `DeleteMcpConfigResponse`: An empty response. **Node.js SDK:** `scalekit.actions.mcp.deleteConfig` Deletes an MCP configuration. ```ts scalekit.actions.mcp.deleteConfig( configId: string, ): Promise ``` Example: ```ts const result = await scalekit.actions.mcp.deleteConfig("cfg_85630864460904897"); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `configId` | `string` | Yes | ID of the configuration to delete. | Returns `Promise`. **Used in** - [Create a Virtual MCP server](https://docs.scalekit.com/agentkit/mcp/configure-mcp-server/) ## Check a user's connected accounts for a server `POST /api/v1/mcp/configs/{config_id}/connected_accounts` Returns the user's connected account on each connection the server uses, with its status. Call it before you mint a session token, and send the user the authorization link for any account that isn't `ACTIVE`. With `include_auth_link` set to `true`, the call creates any account the user doesn't have yet and returns a fresh [authorization link](https://docs.scalekit.com/agentkit/reference/authorization/get-an-authorization-link/) for every connection, in `authentication_link`. Without it, nothing is created, and a connection where the user has no account comes back with an empty `connected_account_id`. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `config_id` | string | Yes | ID of the MCP configuration. | **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `identifier` | string | Yes | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `include_auth_link` | boolean | No | Set to `true` to get a fresh authorization link for every connection, creating any connected account the user doesn't have yet. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `connected_accounts` | array of object | Connected account state for each connection in the configuration. | | `connected_accounts.authentication_link` | string | The authorization link for this connection. Empty unless the request set `include_auth_link`. | | `connected_accounts.connected_account_id` | string | The user's connected account on this connection. Empty when the user has none. | | `connected_accounts.connected_account_status` | string | The account's status, such as `ACTIVE` or `PENDING_AUTH`. Empty when the user has no account. | | `connected_accounts.connection_id` | string | ID of the connection. | | `connected_accounts.connection_name` | string | Name of the connection. | | `connected_accounts.provider` | string | The app, such as `GITHUB`. | **Errors** - `400`: Bad request - config_id or identifier is missing or invalid - `404`: Not found - no MCP configuration exists with the given config_id **Request** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/mcp/configs/cfg_85630864460904897/connected_accounts" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "identifier": "user_123", "include_auth_link": true }' ``` **Response** ```json { "connected_accounts": [ { "connection_id": "conn_70219645518267719", "connection_name": "github-connect", "provider": "GITHUB", "connected_account_id": "ca_24834495392091352", "connected_account_status": "PENDING_AUTH", "authentication_link": "https://your-env.scalekit.dev/magicLink/7f0c2b9e-4d1a-4c3e-9b8f-2a6d5e1c3f40_o" } ] } ``` **Python SDK:** `scalekit_client.actions.mcp.list_mcp_connected_accounts` Lists the user's connected account on each connection a Virtual MCP server uses. Returns each account's status and, with `include_auth_link=True`, an authorization link the user opens to connect or reconnect. Call it before you mint a session token. ```python scalekit_client.actions.mcp.list_mcp_connected_accounts( config_id: str, identifier: str, include_auth_link: Optional[bool] = None, ) -> ListMcpConnectedAccountsResponse ``` Example: ```python result = scalekit_client.actions.mcp.list_mcp_connected_accounts( config_id="cfg_85630864460904897", identifier="user_123", include_auth_link=True, ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `config_id` | `str` | Yes | Scalekit ID of the MCP configuration to inspect, e.g. `"cfg_01abc123"`. | | `identifier` | `str` | Yes | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `include_auth_link` | `Optional[bool]` | No | Set to `true` to get a fresh authorization link for every connection, creating any connected account the user doesn't have yet. | Returns `ListMcpConnectedAccountsResponse`: One entry per connection in `connected_accounts`, with `connection_name`, `provider`, `connected_account_id`, `connected_account_status`, such as `ACTIVE` or `PENDING_AUTH`, and `authentication_link`, the authorization link when you set `include_auth_link`. **Node.js SDK:** `scalekit.actions.mcp.listConnectedAccounts` Lists the connected accounts backing a configuration for one user. Call this before minting a session token: OAuth credentials can expire or be revoked at any time, and an agent that starts without an active connection fails on every tool call. Surface `authenticationLink` for any account whose `connectedAccountStatus` is not `"ACTIVE"`. ```ts scalekit.actions.mcp.listConnectedAccounts( params: { configId: string; identifier: string; includeAuthLink?: boolean; }, ): Promise ``` Example: ```ts const result = await scalekit.actions.mcp.listConnectedAccounts({ configId: "cfg_85630864460904897", identifier: "user_123", includeAuthLink: true, }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `configId` | `string` | Yes | ID of the configuration. | | `identifier` | `string` | Yes | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `includeAuthLink` | `boolean` | No | Set to `true` to get a fresh authorization link for every connection, creating any connected account the user doesn't have yet. | Returns `Promise`. **Used in** - [Claude Managed Agents](https://docs.scalekit.com/agentkit/examples/claude-managed-agents/) - [Mint session tokens](https://docs.scalekit.com/agentkit/mcp/session-tokens/) ## Mint a session token `POST /api/v1/mcp/configs/{mcp_config_id}/tokens` Mints a short-lived JWT that represents a user identifier across the connected accounts associated with an MCP configuration. The supplied identifier becomes the token's `sub` claim; the token's `aud` claim is the MCP server URL bound to the configuration. Claims also carry the MCP configuration ID (`mcp_cfg`) and the list of resolved connected-account IDs (`ca_ids`). Use this operation to issue a single credential an MCP server can present on the user's behalf when calling provider tools. The mint fails if any connection mapped to the configuration has no active connected account for the identifier. For a `META` server, the user needn't have connected accounts yet. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `mcp_config_id` | string | Yes | Unique ID of the MCP configuration whose connections back the token. The configuration must exist in the caller's environment. | **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `identifier` | string | Yes | Your app's ID for the user, the same value you used when the user connected. | | `access_level` | string (enum) | No | Which tools the token can use: `FULL` (the default) or `READ_ONLY`, in capitals. A `READ_ONLY` token sees and calls only the server's read-only tools. See [Limit an agent to read-only tools](https://docs.scalekit.com/agentkit/mcp/read-only-tokens/). One of: `FULL`, `READ_ONLY`. | | `expiry` | string | No | How long the token lasts, in seconds with an `s` suffix, such as `1800s`. Between `60s` and `86400s` (24 hours). Defaults to `3600s`. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `expires_at` | string | When the token expires: the time it was minted plus `expiry`. | | `token` | string | The session token, a signed JWT. Its `sub` claim is the identifier and its `aud` claim is the MCP server URL it works at. Send it as `Authorization: Bearer ` from the MCP client. | **Errors** - `400`: Invalid request - mcp_config_id or identifier is missing or malformed, expiry is outside the 60s-24h window, the MCP configuration has no connections, or a connection has no active connected account for the supplied identifier - `404`: Not Found - no MCP configuration exists with the supplied ID in the caller's environment **Request** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/mcp/configs/cfg_85630864460904897/tokens" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "identifier": "user_123", "expiry": "1800s" }' ``` **Response** ```json { "token": "[SESSION TOKEN]", "expires_at": "2026-10-02T15:00:00Z" } ``` **Python SDK:** `scalekit_client.actions.mcp.create_session_token` Create a short-lived session token for a user to access an MCP server. Pass exactly one of `mcp_config_id` or `connection_name`; the token works only on the MCP server of that target: `mcp_config_id`: the MCP configuration's server, at the config's `mcp_server_url`. It serves the tools the configuration exposes. `connection_name`: the connection's own MCP server, at `/mcp/v3/connections/`. It serves every tool of that connection, with no MCP configuration. Send the token as a `Bearer` token in the `Authorization` header when calling that URL. ```python scalekit_client.actions.mcp.create_session_token( mcp_config_id: str | None = None, identifier: str | None = None, expiry: timedelta | None = None, access_level: str | None = None, connection_name: str | None = None, ) -> CreateMcpSessionTokenResponse ``` Example: ```python from datetime import timedelta result = scalekit_client.actions.mcp.create_session_token( mcp_config_id="cfg_85630864460904897", identifier="user_123", expiry=timedelta(seconds=1800), ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `mcp_config_id` | `str \| None` | No | Scalekit ID of the MCP configuration the token grants access to, e.g. `"cfg_01abc123"`. | | `identifier` | `str \| None` | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `expiry` | `timedelta \| None` | No | Requested lifetime for the token as a Python `timedelta`. The server accepts 60 seconds to 24 hours and applies 1 hour when omitted. Fractions of a second are dropped. Example values: `timedelta(minutes=30)` — 30-minute token; `timedelta(hours=8)` — 8-hour token (work-day session); `timedelta(days=1)` — 24-hour token | | `access_level` | `str \| None` | No | Tools the token can use. `"READ_ONLY"` limits it to tools annotated read-only: other tools are left out of the tool list and refused when called. `"FULL"`, or omitting it, exposes every tool of the target. Values are case-sensitive. | | `connection_name` | `str \| None` | No | The connection name, as shown in **AgentKit** > **Connections**. | Returns `CreateMcpSessionTokenResponse`: The session `token`, a signed JWT, and `expires_at`, when it expires (UTC). **Node.js SDK:** `scalekit.actions.mcp.createSessionToken` Mints a session token for one user, for either a Virtual MCP server or a connection's own MCP server. Set a target; if both are set, `mcpConfigId` is used: `mcpConfigId`: the token works on that configuration's `config.mcpServerUrl` (see `getConfig`). `connectionName`: the token works on the connection's own MCP server at `/mcp/v3/connections/`, which exposes all of that connection's tools without an MCP configuration. The name is matched without regard to case, but the URL path is case-sensitive: build it from the connection's name exactly as stored. A token works only on the server it was minted for. The server URL is static; the token is what carries user identity. Mint a fresh one before every agent run and never reuse one across runs. Set the expiry longer than the run is expected to take. For a connection, the user should have an active connected account on it. When they do not, the server either rejects the call, or creates a pending connected account and returns a token whose tool calls report the account as not connected, depending on the environment. ```ts scalekit.actions.mcp.createSessionToken( params: { identifier: string; expirySeconds?: number; accessLevel?: McpSessionTokenAccessLevel; mcpConfigId?: string; connectionName?: string; }, ): Promise ``` Example: ```ts const result = await scalekit.actions.mcp.createSessionToken({ identifier: "user_123", expirySeconds: 1800, mcpConfigId: "cfg_85630864460904897", }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `identifier` | `string` | Yes | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `expirySeconds` | `number` | No | Token lifetime in whole seconds, 60 to 86400. Defaults to 3600. | | `accessLevel` | `McpSessionTokenAccessLevel` | No | Tools the token can use. Omit for `'FULL'`. | | `mcpConfigId` | `string` | No | ID of the MCP configuration whose server the token is for. | | `connectionName` | `string` | No | The connection name, as shown in **AgentKit** > **Connections**. | Returns `Promise`: The session `token` and its `expiresAt` time. **Used in** - [Anthropic](https://docs.scalekit.com/agentkit/examples/anthropic/) - [Build a multi-agent email triage crew with CrewAI](https://docs.scalekit.com/cookbooks/crewai-agentkit-email-triage/) - [Build a Vapi voice assistant with Scalekit](https://docs.scalekit.com/cookbooks/build-voice-assistant-1000-tools/) - [Claude Managed Agents](https://docs.scalekit.com/agentkit/examples/claude-managed-agents/) - [CrewAI](https://docs.scalekit.com/agentkit/examples/crewai/) - [Google ADK](https://docs.scalekit.com/agentkit/examples/google-adk/) - [LangChain](https://docs.scalekit.com/agentkit/examples/langchain/) - [LangGraph](https://docs.scalekit.com/agentkit/examples/langgraph/) - [Limit an agent to read-only tools](https://docs.scalekit.com/agentkit/mcp/read-only-tokens/) - [Mastra](https://docs.scalekit.com/agentkit/examples/mastra/) - [Mint session tokens](https://docs.scalekit.com/agentkit/mcp/session-tokens/) - [OpenAI Agents SDK](https://docs.scalekit.com/agentkit/examples/openai-agents-sdk/) - [Use a connection's MCP server](https://docs.scalekit.com/agentkit/mcp/connection-mcp-servers/) ## Mint a session token for a connection `POST /api/v1/mcp/connections/{key_id}/tokens` Mints a short-lived token for one user that an MCP client sends to one connection's MCP server, at `/mcp/v3/connections/{key_id}`, without a Virtual MCP server. The token's `sub` claim is the identifier and its `aud` claim is that server URL, so the token works only there. Use it when an agent needs the tools of exactly one connection. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `key_id` | string | Yes | The connection name, as shown in AgentKit > Connections. It's also the last path segment of the connection's MCP server URL. | **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `identifier` | string | Yes | Your app's ID for the user, the same value you used when the user connected. | | `access_level` | string (enum) | No | Which tools the token can use: `FULL` (the default) or `READ_ONLY`, in capitals. A `READ_ONLY` token sees and calls only the server's read-only tools. See [Limit an agent to read-only tools](https://docs.scalekit.com/agentkit/mcp/read-only-tokens/). One of: `FULL`, `READ_ONLY`. | | `expiry` | string | No | How long the token lasts, in seconds with an `s` suffix, such as `1800s`. Between `60s` and `86400s` (24 hours). Defaults to `3600s`. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `expires_at` | string | When the token expires: the time it was minted plus `expiry`. | | `token` | string | The session token, a signed JWT. Its `sub` claim is the identifier and its `aud` claim is the MCP server URL it works at. Send it as `Authorization: Bearer ` from the MCP client. | **Errors** - `400`: Invalid request - `key_id` or `identifier` is missing or malformed, `expiry` is outside the 60s-24h window, `access_level` isn't `FULL` or `READ_ONLY`, the connection isn't an AgentKit connection, or the user has no active account on the connection. Depending on the environment, Scalekit instead creates a pending account for the user and returns a token, so the user can authorize from the MCP client. - `404`: Not found - no active connection with this name exists in the environment. **Request** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/mcp/connections/gmail/tokens" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "identifier": "user_123", "expiry": "1800s" }' ``` **Response** ```json { "token": "[SESSION TOKEN]", "expires_at": "2026-10-02T15:00:00Z" } ``` **Python SDK:** `scalekit_client.actions.mcp.create_session_token` Create a short-lived session token for a user to access an MCP server. Pass exactly one of `mcp_config_id` or `connection_name`; the token works only on the MCP server of that target: `mcp_config_id`: the MCP configuration's server, at the config's `mcp_server_url`. It serves the tools the configuration exposes. `connection_name`: the connection's own MCP server, at `/mcp/v3/connections/`. It serves every tool of that connection, with no MCP configuration. Send the token as a `Bearer` token in the `Authorization` header when calling that URL. ```python scalekit_client.actions.mcp.create_session_token( mcp_config_id: str | None = None, identifier: str | None = None, expiry: timedelta | None = None, access_level: str | None = None, connection_name: str | None = None, ) -> CreateMcpSessionTokenResponse ``` Example: ```python from datetime import timedelta result = scalekit_client.actions.mcp.create_session_token( identifier="user_123", expiry=timedelta(seconds=1800), connection_name="gmail", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `mcp_config_id` | `str \| None` | No | Scalekit ID of the MCP configuration the token grants access to, e.g. `"cfg_01abc123"`. | | `identifier` | `str \| None` | No | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `expiry` | `timedelta \| None` | No | Requested lifetime for the token as a Python `timedelta`. The server accepts 60 seconds to 24 hours and applies 1 hour when omitted. Fractions of a second are dropped. Example values: `timedelta(minutes=30)` — 30-minute token; `timedelta(hours=8)` — 8-hour token (work-day session); `timedelta(days=1)` — 24-hour token | | `access_level` | `str \| None` | No | Tools the token can use. `"READ_ONLY"` limits it to tools annotated read-only: other tools are left out of the tool list and refused when called. `"FULL"`, or omitting it, exposes every tool of the target. Values are case-sensitive. | | `connection_name` | `str \| None` | No | The connection name, as shown in **AgentKit** > **Connections**. | Returns `CreateMcpSessionTokenResponse`: The session `token`, a signed JWT, and `expires_at`, when it expires (UTC). **Node.js SDK:** `scalekit.actions.mcp.createSessionToken` Mints a session token for one user, for either a Virtual MCP server or a connection's own MCP server. Set a target; if both are set, `mcpConfigId` is used: `mcpConfigId`: the token works on that configuration's `config.mcpServerUrl` (see `getConfig`). `connectionName`: the token works on the connection's own MCP server at `/mcp/v3/connections/`, which exposes all of that connection's tools without an MCP configuration. The name is matched without regard to case, but the URL path is case-sensitive: build it from the connection's name exactly as stored. A token works only on the server it was minted for. The server URL is static; the token is what carries user identity. Mint a fresh one before every agent run and never reuse one across runs. Set the expiry longer than the run is expected to take. For a connection, the user should have an active connected account on it. When they do not, the server either rejects the call, or creates a pending connected account and returns a token whose tool calls report the account as not connected, depending on the environment. ```ts scalekit.actions.mcp.createSessionToken( params: { identifier: string; expirySeconds?: number; accessLevel?: McpSessionTokenAccessLevel; mcpConfigId?: string; connectionName?: string; }, ): Promise ``` Example: ```ts const result = await scalekit.actions.mcp.createSessionToken({ identifier: "user_123", expirySeconds: 1800, connectionName: "gmail", }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `identifier` | `string` | Yes | Your app's ID for the user, the same value you used when the user connected. Use a stable internal ID, not an email address. | | `expirySeconds` | `number` | No | Token lifetime in whole seconds, 60 to 86400. Defaults to 3600. | | `accessLevel` | `McpSessionTokenAccessLevel` | No | Tools the token can use. Omit for `'FULL'`. | | `mcpConfigId` | `string` | No | ID of the MCP configuration whose server the token is for. | | `connectionName` | `string` | No | The connection name, as shown in **AgentKit** > **Connections**. | Returns `Promise`: The session `token` and its `expiresAt` time. **Used in** - [Anthropic](https://docs.scalekit.com/agentkit/examples/anthropic/) - [Build a multi-agent email triage crew with CrewAI](https://docs.scalekit.com/cookbooks/crewai-agentkit-email-triage/) - [Build a Vapi voice assistant with Scalekit](https://docs.scalekit.com/cookbooks/build-voice-assistant-1000-tools/) - [Claude Managed Agents](https://docs.scalekit.com/agentkit/examples/claude-managed-agents/) - [CrewAI](https://docs.scalekit.com/agentkit/examples/crewai/) - [Google ADK](https://docs.scalekit.com/agentkit/examples/google-adk/) - [LangChain](https://docs.scalekit.com/agentkit/examples/langchain/) - [LangGraph](https://docs.scalekit.com/agentkit/examples/langgraph/) - [Limit an agent to read-only tools](https://docs.scalekit.com/agentkit/mcp/read-only-tokens/) - [Mastra](https://docs.scalekit.com/agentkit/examples/mastra/) - [Mint session tokens](https://docs.scalekit.com/agentkit/mcp/session-tokens/) - [OpenAI Agents SDK](https://docs.scalekit.com/agentkit/examples/openai-agents-sdk/) - [Use a connection's MCP server](https://docs.scalekit.com/agentkit/mcp/connection-mcp-servers/) --- Source: https://docs.scalekit.com/agentkit/reference/custom-connectors.md # Custom connectors These endpoints manage custom connectors: they tell Scalekit how to authenticate to an app that isn't in the [catalog](https://docs.scalekit.com/agentkit/connectors/). In the API a connector is a provider. For an app in the catalog, [set up a connection](https://docs.scalekit.com/agentkit/connections/) instead. See [Create a connector](https://docs.scalekit.com/agentkit/bring-your-own-connector/create-connector/). One page per endpoint, each with its own markdown copy: | Endpoint | Request | What it does | Python SDK | Node.js SDK | | --- | --- | --- | --- | --- | | [Create a custom connector](https://docs.scalekit.com/agentkit/reference/custom-connectors/create-a-custom-connector.md) | `POST /api/v1/custom-providers` | Register an app that isn't in the catalog, and how users sign in to it. | `actions.providers.create_custom_provider` | `actions.providers.createCustomProvider` | | [Update a custom connector](https://docs.scalekit.com/agentkit/reference/custom-connectors/update-a-custom-connector.md) | `PUT /api/v1/custom-providers/{identifier}` | Replace a connector's name, auth patterns, API base URL or metadata. | `actions.providers.update_custom_provider` | `actions.providers.updateCustomProvider` | | [Delete a custom connector](https://docs.scalekit.com/agentkit/reference/custom-connectors/delete-a-custom-connector.md) | `DELETE /api/v1/custom-providers/{identifier}` | Delete a connector that no connection uses any more. | `actions.providers.delete_custom_provider` | `actions.providers.deleteCustomProvider` | ## Create a custom connector `POST /api/v1/custom-providers` Creates an environment-scoped custom provider (connector) with authentication patterns and optional proxy configuration. The returned identifier must be used for all subsequent update and delete operations on this provider. **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `auth_patterns` | array of object | Yes | Authentication patterns for the connected app provider. | | `auth_patterns.display_name` | string | Yes | Name of the method, shown to the user while they connect. | | `auth_patterns.type` | string (enum) | Yes | How users authenticate. Use `NO_AUTH` only for a public MCP server, with `is_mcp` set and no `fields`. The `type` of an existing pattern can't change. One of: `OAUTH`, `BEARER`, `API_KEY`, `BASIC`, `NO_AUTH`. | | `auth_patterns.account_fields` | array of object | No | Inputs collected for an `OAUTH` pattern, such as a value `proxy_url` needs. Same fields as `fields`. | | `auth_patterns.auth_field_mutations` | object | No | A `prefix`, `suffix` or `default` applied to a credential before Scalekit sends it, keyed by `api_key`, `token`, `username` or `password`. | | `auth_patterns.auth_header_key_override` | string | No | Header to send the credential in when the app doesn't use `Authorization`, such as `X-API-Key`. | | `auth_patterns.description` | string | No | Short explanation of the method. | | `auth_patterns.fields` | array of object | No | Inputs the user fills in for `BEARER`, `API_KEY` and `BASIC`. Leave it empty for `OAUTH` and `NO_AUTH`. | | `auth_patterns.fields.field_name` | string | Yes | Key the value is stored under: `token` for `BEARER`, `api_key` for `API_KEY`, `username` and `password` for `BASIC`, or a name such as `domain` that `proxy_url` uses as `{{domain}}`. | | `auth_patterns.fields.hint` | string | No | Helper text shown with the input. | | `auth_patterns.fields.input_type` | string (enum) | No | Use `password` for secrets, so the input is masked. One of: `text`, `password`, `select`. | | `auth_patterns.fields.label` | string | No | Label shown above the input. | | `auth_patterns.fields.options` | array of object | No | Choices for a `select` input, each with `value`, `display_name`, `description` and `default`. | | `auth_patterns.fields.required` | boolean | No | Whether the user must fill in the input before connecting. | | `auth_patterns.is_mcp` | boolean | No | Set to `true` when `proxy_url` is an MCP server. Set it on every pattern; it can't change later. | | `auth_patterns.oauth_config` | object | No | OAuth settings for an `OAUTH` pattern. For an MCP server, `{"pkce_enabled": true}` is enough: the server handles authorization. | | `auth_patterns.oauth_config.authorize_uri` | string | No | The app's authorization endpoint. | | `auth_patterns.oauth_config.available_scopes` | array of object | No | Scopes the connection can request, each with `scope`, `display_name`, `description` and `required`. | | `auth_patterns.oauth_config.pkce_enabled` | boolean | No | Whether the authorization flow uses PKCE. | | `auth_patterns.oauth_config.token_uri` | string | No | The app's token endpoint. | | `auth_patterns.oauth_config.user_info_uri` | string | No | Endpoint that returns the signed-in user. | | `display_name` | string | Yes | Display name for the connected app provider. | | `proxy_url` | string | Yes | Proxy URL for the connected app provider. Must start with https://. | | `description` | string | No | Description of the connected app provider. | | `icon_src` | string | No | URL for the provider icon. Should be an SVG image sized 800x800 pixels for best rendering experience. | | `metadata` | object | No | Custom key-value metadata for this provider. Keys must be 3-25 characters, values must be 1-256 characters, with a maximum of 20 key-value pairs. | | `proxy_enabled` | boolean | No | This flag indicates whether proxying is turned on for the connected app provider. When enabled, requests are routed through the provider proxy instead of being sent directly. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `provider` | object | The connector. | | `provider.auth_patterns` | array of object | How users authenticate, as sent in the request. | | `provider.auth_patterns.account_fields` | array of object | Inputs collected for an `OAUTH` pattern, such as a value `proxy_url` needs. Same fields as `fields`. | | `provider.auth_patterns.auth_field_mutations` | object | A `prefix`, `suffix` or `default` applied to a credential before Scalekit sends it, keyed by `api_key`, `token`, `username` or `password`. | | `provider.auth_patterns.auth_header_key_override` | string | Header to send the credential in when the app doesn't use `Authorization`, such as `X-API-Key`. | | `provider.auth_patterns.description` | string | Short explanation of the method. | | `provider.auth_patterns.display_name` | string | Name of the method, shown to the user while they connect. | | `provider.auth_patterns.fields` | array of object | Inputs the user fills in for `BEARER`, `API_KEY` and `BASIC`. Leave it empty for `OAUTH` and `NO_AUTH`. | | `provider.auth_patterns.fields.field_name` | string | Key the value is stored under: `token` for `BEARER`, `api_key` for `API_KEY`, `username` and `password` for `BASIC`, or a name such as `domain` that `proxy_url` uses as `{{domain}}`. | | `provider.auth_patterns.fields.hint` | string | Helper text shown with the input. | | `provider.auth_patterns.fields.input_type` | string (enum) | Use `password` for secrets, so the input is masked. One of: `text`, `password`, `select`. | | `provider.auth_patterns.fields.label` | string | Label shown above the input. | | `provider.auth_patterns.fields.options` | array of object | Choices for a `select` input, each with `value`, `display_name`, `description` and `default`. | | `provider.auth_patterns.fields.required` | boolean | Whether the user must fill in the input before connecting. | | `provider.auth_patterns.is_mcp` | boolean | Set to `true` when `proxy_url` is an MCP server. Set it on every pattern; it can't change later. | | `provider.auth_patterns.oauth_config` | object | OAuth settings for an `OAUTH` pattern. For an MCP server, `{"pkce_enabled": true}` is enough: the server handles authorization. | | `provider.auth_patterns.oauth_config.authorize_uri` | string | The app's authorization endpoint. | | `provider.auth_patterns.oauth_config.available_scopes` | array of object | Scopes the connection can request, each with `scope`, `display_name`, `description` and `required`. | | `provider.auth_patterns.oauth_config.pkce_enabled` | boolean | Whether the authorization flow uses PKCE. | | `provider.auth_patterns.oauth_config.token_uri` | string | The app's token endpoint. | | `provider.auth_patterns.oauth_config.user_info_uri` | string | Endpoint that returns the signed-in user. | | `provider.auth_patterns.type` | string (enum) | How users authenticate. Use `NO_AUTH` only for a public MCP server, with `is_mcp` set and no `fields`. The `type` of an existing pattern can't change. One of: `OAUTH`, `BEARER`, `API_KEY`, `BASIC`, `NO_AUTH`. | | `provider.categories` | array of string | Catalog categories. Empty for a custom connector. | | `provider.coming_soon` | boolean | Whether the catalog lists the connector as coming soon. | | `provider.description` | string | Short description of the app. | | `provider.display_name` | string | Name shown to users. | | `provider.display_priority` | integer | Where the connector sorts in lists; lower numbers come first. | | `provider.icon_src` | string | URL of the connector's icon. | | `provider.id` | string | Scalekit's ID for the connector. | | `provider.identifier` | string | Pass it to update and delete the connector. Scalekit derives it from `display_name`. | | `provider.is_custom` | boolean | Indicates whether the provider is environment-scoped (custom provider). | | `provider.is_custom_mcp` | boolean | Indicates whether this is an environment-scoped MCP-based custom provider. | | `provider.metadata` | object | Custom key-value metadata stored for this provider. Returned for all providers; defaults to {} when no metadata has been set. | | `provider.proxy_enabled` | boolean | Whether Scalekit proxies requests to `proxy_url`. | | `provider.proxy_url` | string | Base URL of the API that the proxy calls. | **Errors** - `400`: Invalid request - the provider payload failed validation (e.g. missing required fields or invalid proxy URL) **Request** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/custom-providers" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "display_name": "Example API", "description": "Connect to an API that accepts a static bearer token", "auth_patterns": [ { "type": "BEARER", "display_name": "Bearer Token", "description": "Authenticate with a static bearer token", "fields": [ { "field_name": "token", "label": "Bearer Token", "input_type": "password", "hint": "Your long-lived bearer token", "required": true } ] } ], "proxy_url": "https://api.example.com", "proxy_enabled": true }' ``` **Response** ```json { "provider": { "id": "prvd_84718296317953812", "identifier": "EXAMPLEAPI:12345", "display_name": "Example API", "description": "Connect to an API that accepts a static bearer token", "auth_patterns": [ { "type": "BEARER", "display_name": "Bearer Token", "description": "Authenticate with a static bearer token", "fields": [ { "field_name": "token", "label": "Bearer Token", "input_type": "password", "hint": "Your long-lived bearer token", "required": true } ] } ], "proxy_url": "https://api.example.com", "proxy_enabled": true, "categories": [], "icon_src": "", "display_priority": 1, "coming_soon": false, "is_custom": true, "is_custom_mcp": false, "metadata": {} } } ``` **Python SDK:** `scalekit_client.actions.providers.create_custom_provider` Create a new custom provider. ```python scalekit_client.actions.providers.create_custom_provider( request: CreateCustomProviderRequest, ) -> CreateCustomProviderResponse ``` Example: ```python from scalekit.actions.types import CreateCustomProviderRequest result = scalekit_client.actions.providers.create_custom_provider( request=CreateCustomProviderRequest( display_name="Example API", description="Connect to an API that accepts a static bearer token", auth_patterns=[ { "type": "BEARER", "display_name": "Bearer Token", "description": "Authenticate with a static bearer token", "fields": [ { "field_name": "token", "label": "Bearer Token", "input_type": "password", "hint": "Your long-lived bearer token", "required": True, }, ], }, ], proxy_url="https://api.example.com", proxy_enabled=True, ), ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `request` | `CreateCustomProviderRequest` | Yes | Request object containing display_name (required), proxy_url (required), and optional description, proxy_enabled, auth_patterns, icon_src, and metadata. | Returns `CreateCustomProviderResponse`: The created provider with its server-assigned identifier and all decoded auth_patterns. **Node.js SDK:** `scalekit.actions.providers.createCustomProvider` Creates a custom connector. ```ts scalekit.actions.providers.createCustomProvider( params: { displayName: string; proxyUrl: string; proxyEnabled?: boolean; description?: string; authPatterns: AuthPattern[]; iconSrc?: string; metadata?: Record; }, ): Promise ``` Example: ```ts const result = await scalekit.actions.providers.createCustomProvider({ displayName: "Example API", proxyUrl: "https://api.example.com", proxyEnabled: true, description: "Connect to an API that accepts a static bearer token", authPatterns: [ { type: "BEARER", display_name: "Bearer Token", description: "Authenticate with a static bearer token", fields: [ { field_name: "token", label: "Bearer Token", input_type: "password", hint: "Your long-lived bearer token", required: true, }, ], }, ], }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `displayName` | `string` | Yes | Human-readable name. Letters, digits and spaces only. Suffix it with "MCP" when the connector fronts an MCP server. | | `proxyUrl` | `string` | Yes | Base HTTPS URL of the upstream service. | | `proxyEnabled` | `boolean` | No | Whether Scalekit proxies requests. Defaults to true. | | `description` | `string` | No | Description of the connected app provider | | `authPatterns` | `AuthPattern[]` | Yes | How users authenticate: one pattern, with its `type`, `display_name` and the `fields` users fill in. | | `iconSrc` | `string` | No | URL for the provider icon. Should be an SVG image sized 800x800 pixels for best rendering experience. | | `metadata` | `Record` | No | Custom key-value metadata for this provider. Keys must be 3-25 characters, values must be 1-256 characters, with a maximum of 20 key-value pairs. | Returns `Promise`: `provider.identifier` on the response is the id you pass to `updateCustomProvider` and `deleteCustomProvider`. **Used in** - [Create a connector](https://docs.scalekit.com/agentkit/bring-your-own-connector/create-connector/) ## Update a custom connector `PUT /api/v1/custom-providers/{identifier}` Updates a custom connector by its `identifier`. Send `display_name`, `proxy_url` and `auth_patterns` on every update. `auth_patterns` and `metadata` replace the stored values, so send everything you want to keep, starting from the create response. `description` and `icon_src` keep their stored values when left out. The pattern `type` and `is_mcp` can't change. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `identifier` | string | Yes | The connector's `identifier`, from the create response. | **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `auth_patterns` | array of object | Yes | Authentication patterns for the connected app provider. | | `auth_patterns.display_name` | string | Yes | Name of the method, shown to the user while they connect. | | `auth_patterns.type` | string (enum) | Yes | How users authenticate. Use `NO_AUTH` only for a public MCP server, with `is_mcp` set and no `fields`. The `type` of an existing pattern can't change. One of: `OAUTH`, `BEARER`, `API_KEY`, `BASIC`, `NO_AUTH`. | | `auth_patterns.account_fields` | array of object | No | Inputs collected for an `OAUTH` pattern, such as a value `proxy_url` needs. Same fields as `fields`. | | `auth_patterns.auth_field_mutations` | object | No | A `prefix`, `suffix` or `default` applied to a credential before Scalekit sends it, keyed by `api_key`, `token`, `username` or `password`. | | `auth_patterns.auth_header_key_override` | string | No | Header to send the credential in when the app doesn't use `Authorization`, such as `X-API-Key`. | | `auth_patterns.description` | string | No | Short explanation of the method. | | `auth_patterns.fields` | array of object | No | Inputs the user fills in for `BEARER`, `API_KEY` and `BASIC`. Leave it empty for `OAUTH` and `NO_AUTH`. | | `auth_patterns.fields.field_name` | string | Yes | Key the value is stored under: `token` for `BEARER`, `api_key` for `API_KEY`, `username` and `password` for `BASIC`, or a name such as `domain` that `proxy_url` uses as `{{domain}}`. | | `auth_patterns.fields.hint` | string | No | Helper text shown with the input. | | `auth_patterns.fields.input_type` | string (enum) | No | Use `password` for secrets, so the input is masked. One of: `text`, `password`, `select`. | | `auth_patterns.fields.label` | string | No | Label shown above the input. | | `auth_patterns.fields.options` | array of object | No | Choices for a `select` input, each with `value`, `display_name`, `description` and `default`. | | `auth_patterns.fields.required` | boolean | No | Whether the user must fill in the input before connecting. | | `auth_patterns.is_mcp` | boolean | No | Set to `true` when `proxy_url` is an MCP server. Set it on every pattern; it can't change later. | | `auth_patterns.oauth_config` | object | No | OAuth settings for an `OAUTH` pattern. For an MCP server, `{"pkce_enabled": true}` is enough: the server handles authorization. | | `auth_patterns.oauth_config.authorize_uri` | string | No | The app's authorization endpoint. | | `auth_patterns.oauth_config.available_scopes` | array of object | No | Scopes the connection can request, each with `scope`, `display_name`, `description` and `required`. | | `auth_patterns.oauth_config.pkce_enabled` | boolean | No | Whether the authorization flow uses PKCE. | | `auth_patterns.oauth_config.token_uri` | string | No | The app's token endpoint. | | `auth_patterns.oauth_config.user_info_uri` | string | No | Endpoint that returns the signed-in user. | | `display_name` | string | Yes | Display name for the connected app provider. | | `proxy_url` | string | Yes | Proxy URL for the connected app provider. Must start with https://. | | `description` | string | No | Description of the connected app provider. | | `icon_src` | string | No | URL for the provider icon. Should be an SVG image sized 800x800 pixels for best rendering experience. | | `metadata` | object | No | Custom key-value metadata for this provider. Keys must be 3-25 characters, values must be 1-256 characters, with a maximum of 20 key-value pairs. | | `proxy_enabled` | boolean | No | This flag indicates whether proxying is turned on for the connected app provider. When enabled, requests are routed through the provider proxy instead of being sent directly. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `provider` | object | The connector. | | `provider.auth_patterns` | array of object | How users authenticate, as sent in the request. | | `provider.auth_patterns.account_fields` | array of object | Inputs collected for an `OAUTH` pattern, such as a value `proxy_url` needs. Same fields as `fields`. | | `provider.auth_patterns.auth_field_mutations` | object | A `prefix`, `suffix` or `default` applied to a credential before Scalekit sends it, keyed by `api_key`, `token`, `username` or `password`. | | `provider.auth_patterns.auth_header_key_override` | string | Header to send the credential in when the app doesn't use `Authorization`, such as `X-API-Key`. | | `provider.auth_patterns.description` | string | Short explanation of the method. | | `provider.auth_patterns.display_name` | string | Name of the method, shown to the user while they connect. | | `provider.auth_patterns.fields` | array of object | Inputs the user fills in for `BEARER`, `API_KEY` and `BASIC`. Leave it empty for `OAUTH` and `NO_AUTH`. | | `provider.auth_patterns.fields.field_name` | string | Key the value is stored under: `token` for `BEARER`, `api_key` for `API_KEY`, `username` and `password` for `BASIC`, or a name such as `domain` that `proxy_url` uses as `{{domain}}`. | | `provider.auth_patterns.fields.hint` | string | Helper text shown with the input. | | `provider.auth_patterns.fields.input_type` | string (enum) | Use `password` for secrets, so the input is masked. One of: `text`, `password`, `select`. | | `provider.auth_patterns.fields.label` | string | Label shown above the input. | | `provider.auth_patterns.fields.options` | array of object | Choices for a `select` input, each with `value`, `display_name`, `description` and `default`. | | `provider.auth_patterns.fields.required` | boolean | Whether the user must fill in the input before connecting. | | `provider.auth_patterns.is_mcp` | boolean | Set to `true` when `proxy_url` is an MCP server. Set it on every pattern; it can't change later. | | `provider.auth_patterns.oauth_config` | object | OAuth settings for an `OAUTH` pattern. For an MCP server, `{"pkce_enabled": true}` is enough: the server handles authorization. | | `provider.auth_patterns.oauth_config.authorize_uri` | string | The app's authorization endpoint. | | `provider.auth_patterns.oauth_config.available_scopes` | array of object | Scopes the connection can request, each with `scope`, `display_name`, `description` and `required`. | | `provider.auth_patterns.oauth_config.pkce_enabled` | boolean | Whether the authorization flow uses PKCE. | | `provider.auth_patterns.oauth_config.token_uri` | string | The app's token endpoint. | | `provider.auth_patterns.oauth_config.user_info_uri` | string | Endpoint that returns the signed-in user. | | `provider.auth_patterns.type` | string (enum) | How users authenticate. Use `NO_AUTH` only for a public MCP server, with `is_mcp` set and no `fields`. The `type` of an existing pattern can't change. One of: `OAUTH`, `BEARER`, `API_KEY`, `BASIC`, `NO_AUTH`. | | `provider.categories` | array of string | Catalog categories. Empty for a custom connector. | | `provider.coming_soon` | boolean | Whether the catalog lists the connector as coming soon. | | `provider.description` | string | Short description of the app. | | `provider.display_name` | string | Name shown to users. | | `provider.display_priority` | integer | Where the connector sorts in lists; lower numbers come first. | | `provider.icon_src` | string | URL of the connector's icon. | | `provider.id` | string | Scalekit's ID for the connector. | | `provider.identifier` | string | Pass it to update and delete the connector. Scalekit derives it from `display_name`. | | `provider.is_custom` | boolean | Indicates whether the provider is environment-scoped (custom provider). | | `provider.is_custom_mcp` | boolean | Indicates whether this is an environment-scoped MCP-based custom provider. | | `provider.metadata` | object | Custom key-value metadata stored for this provider. Returned for all providers; defaults to {} when no metadata has been set. | | `provider.proxy_enabled` | boolean | Whether Scalekit proxies requests to `proxy_url`. | | `provider.proxy_url` | string | Base URL of the API that the proxy calls. | **Errors** - `400`: Invalid request - the update payload failed validation - `404`: Not Found - no custom provider exists with the given identifier **Request** ```bash curl -sS -X PUT \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/custom-providers/EXAMPLEAPI:12345" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "display_name": "Example API", "description": "Connect to the Example API with a static bearer token", "auth_patterns": [ { "type": "BEARER", "display_name": "Bearer Token", "description": "Authenticate with a static bearer token", "fields": [ { "field_name": "token", "label": "Bearer Token", "input_type": "password", "hint": "Your long-lived bearer token", "required": true } ] } ], "proxy_url": "https://api.example.com", "proxy_enabled": true }' ``` **Response** ```json { "provider": { "id": "prvd_84718296317953812", "identifier": "EXAMPLEAPI:12345", "display_name": "Example API", "description": "Connect to the Example API with a static bearer token", "auth_patterns": [ { "type": "BEARER", "display_name": "Bearer Token", "description": "Authenticate with a static bearer token", "fields": [ { "field_name": "token", "label": "Bearer Token", "input_type": "password", "hint": "Your long-lived bearer token", "required": true } ] } ], "proxy_url": "https://api.example.com", "proxy_enabled": true, "categories": [], "icon_src": "", "display_priority": 1, "coming_soon": false, "is_custom": true, "is_custom_mcp": false, "metadata": {} } } ``` **Python SDK:** `scalekit_client.actions.providers.update_custom_provider` Update an existing custom provider. Send `display_name`, `proxy_url` and `auth_patterns` on every update. `auth_patterns` and `metadata` replace the stored values, so send everything you want to keep. `description` and `icon_src` keep their stored values when left as `None`. ```python scalekit_client.actions.providers.update_custom_provider( request: UpdateCustomProviderRequest, ) -> UpdateCustomProviderResponse ``` Example: ```python from scalekit.actions.types import UpdateCustomProviderRequest result = scalekit_client.actions.providers.update_custom_provider( request=UpdateCustomProviderRequest( identifier="EXAMPLEAPI:12345", display_name="Example API", description="Connect to the Example API with a static bearer token", auth_patterns=[ { "type": "BEARER", "display_name": "Bearer Token", "description": "Authenticate with a static bearer token", "fields": [ { "field_name": "token", "label": "Bearer Token", "input_type": "password", "hint": "Your long-lived bearer token", "required": True, }, ], }, ], proxy_url="https://api.example.com", ), ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `request` | `UpdateCustomProviderRequest` | Yes | The `identifier` of the connector to update, with `display_name`, `proxy_url` and `auth_patterns`, and optionally `description`, `icon_src` and `metadata`. | Returns `UpdateCustomProviderResponse`: The provider's full state after the update. **Node.js SDK:** `scalekit.actions.providers.updateCustomProvider` Updates a custom connector. Treat this as a PUT: read the current connector with `listProviders` first, then send back every field you want to keep alongside the ones you are changing. `provider.authPatterns` and `provider.proxyEnabled` from that response can be passed here as-is. What the server does with each field: `displayName`, `proxyUrl` and `authPatterns` are required on every update. Omitting `authPatterns` fails with `[invalid_argument] Validation error`, and it replaces the whole list rather than merging into it. The pattern's `type` and `is_mcp` cannot be changed. `metadata` replaces the stored map, so leaving it out clears it. `proxyEnabled` is always applied. It defaults to `true` here, so pass the current value to keep a connector's proxying switched off. `description` and `iconSrc` keep their stored values when left out. ```ts scalekit.actions.providers.updateCustomProvider( params: { identifier: string; displayName: string; proxyUrl: string; authPatterns: AuthPattern[]; proxyEnabled?: boolean; description?: string; iconSrc?: string; metadata?: Record; }, ): Promise ``` Example: ```ts const result = await scalekit.actions.providers.updateCustomProvider({ identifier: "EXAMPLEAPI:12345", displayName: "Example API", proxyUrl: "https://api.example.com", authPatterns: [ { type: "BEARER", display_name: "Bearer Token", description: "Authenticate with a static bearer token", fields: [ { field_name: "token", label: "Bearer Token", input_type: "password", hint: "Your long-lived bearer token", required: true, }, ], }, ], proxyEnabled: true, description: "Connect to the Example API with a static bearer token", }); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `identifier` | `string` | Yes | From `provider.identifier` on a create or list response. | | `displayName` | `string` | Yes | The connector's name, shown to users. Letters, digits and spaces only. | | `proxyUrl` | `string` | Yes | Proxy URL for the connected app provider. Must start with https:// | | `authPatterns` | `AuthPattern[]` | Yes | How users authenticate: one pattern, with its `type`, `display_name` and the `fields` users fill in. Send it on every update: it replaces the stored list. | | `proxyEnabled` | `boolean` | No | Whether Scalekit proxies requests. Defaults to true. | | `description` | `string` | No | Short description of the app. Keeps its stored value when left out. | | `iconSrc` | `string` | No | URL for the provider icon. Should be an SVG image sized 800x800 pixels for best rendering experience. | | `metadata` | `Record` | No | Custom key-value metadata for this provider. Keys must be 3-25 characters, values must be 1-256 characters, with a maximum of 20 key-value pairs. | Returns `Promise`. **Used in** - [Create a connector](https://docs.scalekit.com/agentkit/bring-your-own-connector/create-connector/) ## Delete a custom connector `DELETE /api/v1/custom-providers/{identifier}` Deletes a custom connector by its `identifier`. Delete its connections first: while any connection uses the connector, the call returns `400` with `PROVIDER_HAS_EXISTING_CONNECTIONS`. Deletion can't be undone. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `identifier` | string | Yes | The connector's `identifier`, from the create response. | **Errors** - `400`: Invalid request - error code `PROVIDER_HAS_EXISTING_CONNECTIONS` when a connection still uses the connector. Delete its connections first. - `404`: Not Found - no custom provider exists with the given identifier **Request** ```bash curl -sS -X DELETE \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/custom-providers/EXAMPLEAPI:12345" \ -H "Authorization: Bearer $TOKEN" ``` **Python SDK:** `scalekit_client.actions.providers.delete_custom_provider` Delete a custom provider by identifier. Deletion is permanent. Returns an empty response on success. Any error (provider not found, insufficient permissions) raises a ScalekitServerException subclass before this method returns. ```python scalekit_client.actions.providers.delete_custom_provider( request: DeleteCustomProviderRequest, ) -> DeleteCustomProviderResponse ``` Example: ```python from scalekit.actions.types import DeleteCustomProviderRequest result = scalekit_client.actions.providers.delete_custom_provider( request=DeleteCustomProviderRequest(identifier="EXAMPLEAPI:12345"), ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `request` | `DeleteCustomProviderRequest` | Yes | Request object containing the identifier of the provider to delete. | Returns `DeleteCustomProviderResponse`: Empty response confirming deletion. **Node.js SDK:** `scalekit.actions.providers.deleteCustomProvider` Deletes a custom connector. Remove the connector's connections and connected accounts first; the server refuses to delete one that is still in use, with `[invalid_argument] cannot delete custom provider with existing connections`. Connected accounts come off with `actions.deleteConnectedAccount`. The app connection itself has to go from the Scalekit dashboard: this SDK wraps no delete for an environment-scoped connection, and `connection.deleteConnection` takes an `organizationId`, which an app connection does not have. ```ts scalekit.actions.providers.deleteCustomProvider( identifier: string, ): Promise ``` Example: ```ts const result = await scalekit.actions.providers.deleteCustomProvider("EXAMPLEAPI:12345"); ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `identifier` | `string` | Yes | The connector's `identifier`, from the create response. | Returns `Promise`. **Used in** - [Create a connector](https://docs.scalekit.com/agentkit/bring-your-own-connector/create-connector/) --- Source: https://docs.scalekit.com/agentkit/reference/triggers.md # Triggers A trigger tells your app when something changes in a user's connected app, such as a new lead in Salesforce or a message in Slack. Subscribe a connected account, or a whole connection, to a trigger, and Scalekit sends each event to your delivery endpoint. See [How triggers work](https://docs.scalekit.com/agentkit/triggers/overview/) and [Receive trigger events](https://docs.scalekit.com/agentkit/triggers/receive-events/). A webhook trigger also needs a webhook URL for the connection, registered in your app at the provider, and a signing secret for it. For an app that signs its events, that's the app's own secret: [set it](https://docs.scalekit.com/agentkit/reference/triggers/set-a-signing-secret/). For an app that doesn't, Scalekit [generates one](https://docs.scalekit.com/agentkit/reference/triggers/generate-a-signing-secret/). This secret covers the events the app sends to Scalekit. It isn't your delivery endpoint's `whsec_` secret, which Scalekit signs the events it sends to you with. A polling trigger needs neither: Scalekit checks the app on the interval you set. While Triggers isn't turned on for your environment, every endpoint here returns `404`. [Contact us](https://docs.scalekit.com/support/contact-us/) to turn it on. One page per endpoint, each with its own markdown copy: | Endpoint | Request | What it does | Python SDK | | --- | --- | --- | --- | | [List available triggers (beta)](https://docs.scalekit.com/agentkit/reference/triggers/list-available-triggers.md) | `GET /api/v1/connections/{connection_id}/triggers` | List a connection's triggers, and whether each one can run on it. | `actions.triggers.list_available` | | [List accounts eligible for a trigger (beta)](https://docs.scalekit.com/agentkit/reference/triggers/list-eligible-accounts.md) | `GET /api/v1/connections/{connection_id}/triggers/{trigger_name}/eligible-accounts` | List a connection's connected accounts, and whether each can subscribe to a trigger. | `actions.triggers.list_eligible_accounts` | | [List trigger subscriptions (beta)](https://docs.scalekit.com/agentkit/reference/triggers/list-trigger-subscriptions.md) | `GET /api/v1/connections/{connection_id}/trigger-subscriptions` | List a connection's trigger subscriptions, with each one's status. | `actions.triggers.list_subscriptions` | | [Subscribe to a trigger (beta)](https://docs.scalekit.com/agentkit/reference/triggers/create-a-trigger-subscription.md) | `POST /api/v1/connections/{connection_id}/trigger-subscriptions` | Subscribe one connected account, or a whole connection, to a trigger. | `actions.triggers.create_subscription` | | [Subscribe several accounts to a trigger (beta)](https://docs.scalekit.com/agentkit/reference/triggers/batch-create-trigger-subscriptions.md) | `POST /api/v1/connections/{connection_id}/trigger-subscriptions:batchCreate` | Subscribe up to 100 connected accounts to a trigger, with a result for each. | `actions.triggers.batch_create_subscriptions` | | [Delete a trigger subscription (beta)](https://docs.scalekit.com/agentkit/reference/triggers/delete-a-trigger-subscription.md) | `DELETE /api/v1/connections/{connection_id}/trigger-subscriptions/{subscription_id}` | Delete one trigger subscription. | `actions.triggers.delete_subscription` | | [Delete several trigger subscriptions (beta)](https://docs.scalekit.com/agentkit/reference/triggers/batch-delete-trigger-subscriptions.md) | `POST /api/v1/connections/{connection_id}/trigger-subscriptions:batchDelete` | Delete up to 100 subscriptions, by ID or by account, with a result for each. | `actions.triggers.batch_delete_subscriptions` | | [Create a webhook URL (beta)](https://docs.scalekit.com/agentkit/reference/triggers/create-a-webhook-url.md) | `POST /api/v1/connections/{connection_id}/trigger-ingress` | Create the URL an app sends a connection's webhook events to. | `actions.triggers.create_ingress_endpoint` | | [Get a webhook URL (beta)](https://docs.scalekit.com/agentkit/reference/triggers/get-a-webhook-url.md) | `GET /api/v1/connections/{connection_id}/trigger-ingress` | Get a connection's webhook URL and the state of its signing secret. | `actions.triggers.get_ingress_endpoint` | | [Set a signing secret (beta)](https://docs.scalekit.com/agentkit/reference/triggers/set-a-signing-secret.md) | `PUT /api/v1/connections/{connection_id}/trigger-ingress/signing-secret` | Save the signing secret the app signs its webhook events with. | `actions.triggers.set_signing_secret` | | [Confirm a signing secret rotation (beta)](https://docs.scalekit.com/agentkit/reference/triggers/confirm-a-signing-secret-rotation.md) | `POST /api/v1/connections/{connection_id}/trigger-ingress/signing-secret/confirm` | Retire the previous signing secret once the app uses the new one. | `actions.triggers.confirm_signing_secret_rotation` | | [Generate a signing secret (beta)](https://docs.scalekit.com/agentkit/reference/triggers/generate-a-signing-secret.md) | `POST /api/v1/connections/{connection_id}/trigger-ingress/signing-secret/generate` | Generate a secret for an app that sends no signature of its own. | `actions.triggers.generate_signing_secret` | | [List deliveries (beta)](https://docs.scalekit.com/agentkit/reference/triggers/list-deliveries.md) | `GET /api/v1/trigger-delivery/endpoints/{endpoint_id}/deliveries` | List the trigger events sent to your delivery endpoint, with their state. | `actions.triggers.list_deliveries` | | [List the attempts for a delivery (beta)](https://docs.scalekit.com/agentkit/reference/triggers/list-attempts-for-a-delivery.md) | `GET /api/v1/trigger-delivery/endpoints/{endpoint_id}/deliveries/{msg_id}/attempts` | List every attempt to deliver one event to your endpoint. | `actions.triggers.list_delivery_attempts_by_message` | | [List recent delivery attempts (beta)](https://docs.scalekit.com/agentkit/reference/triggers/list-recent-delivery-attempts.md) | `GET /api/v1/trigger-delivery/endpoints/{endpoint_id}/attempts` | List the most recent delivery attempts to your endpoint. | `actions.triggers.list_delivery_attempts_by_endpoint` | | [Look up a delivery (beta)](https://docs.scalekit.com/agentkit/reference/triggers/look-up-a-delivery.md) | `GET /api/v1/trigger-delivery/deliveries/{id}` | Find one event by its message ID or event ID, with its delivery attempts. | `actions.triggers.lookup_delivery` | | [Look up several deliveries (beta)](https://docs.scalekit.com/agentkit/reference/triggers/look-up-several-deliveries.md) | `POST /api/v1/trigger-delivery/deliveries:batchLookup` | Find up to 25 events by message ID or event ID, with a result for each. | `actions.triggers.batch_lookup_deliveries` | | [Resend deliveries (beta)](https://docs.scalekit.com/agentkit/reference/triggers/resend-deliveries.md) | `POST /api/v1/trigger-delivery/deliveries:batchResend` | Send up to 50 events to your endpoint again, with a result for each. | `actions.triggers.batch_resend_deliveries` | | [Recover failed deliveries (beta)](https://docs.scalekit.com/agentkit/reference/triggers/recover-failed-deliveries.md) | `POST /api/v1/trigger-delivery/endpoints/{endpoint_id}:recover` | Resend every event that failed to reach your endpoint in a time window. | `actions.triggers.recover_deliveries` | | [Get trigger activity (beta)](https://docs.scalekit.com/agentkit/reference/triggers/get-trigger-activity.md) | `GET /api/v1/trigger-activity` | Count what happened to your environment's trigger events in the last 24 hours. | `actions.triggers.get_activity` | | [Get trigger usage (beta)](https://docs.scalekit.com/agentkit/reference/triggers/get-trigger-usage.md) | `GET /api/v1/trigger-usage` | Get the trigger deliveries metered for your environment, by hour or day. | `actions.triggers.get_usage` | ## List available triggers (beta) `GET /api/v1/connections/{connection_id}/triggers` This endpoint is in beta. Its request and response may change. Lists every trigger the connection's app offers. A trigger that can't run on this connection is still listed, with `available` set to `false` and a `reason` and `remedy`. Each trigger says whether it's a webhook or polling trigger, which scopes it needs, which filters it takes in `config_fields`, and for a polling trigger its default and minimum interval. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | string | Yes | ID of the connection, in this environment. Up to 64 characters. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `connection_uses_shared_app` | boolean | Whether the connection is configured to use Scalekit's shared provider app rather than your own. Read from the connection's saved configuration. When true, any trigger with platform_credentials UNSUPPORTED is unavailable until you add your own provider app to the connection. | | `triggers` | array of object | Every trigger the connection's provider publishes, including unavailable ones. Check available on each before offering it. | | `triggers.available` | boolean | Whether this trigger can run on this connection. False does not mean the trigger is unsupported by the provider — it means something about THIS connection prevents it, and reason and remedy say what. | | `triggers.config_fields` | array of object | Values a subscription can set in its config map to narrow which events it receives. A config key not listed here is refused. | | `triggers.config_fields.display_name` | string | Human-readable label for the field. | | `triggers.config_fields.help_text` | string | Guidance on what to enter. Empty when the trigger provides none. | | `triggers.config_fields.key` | string | The key to send in a subscription's config map. Send it exactly as returned, without changing its case: a key that does not match is refused. | | `triggers.config_fields.placeholder` | string | Example value to show in an empty input. Empty when the trigger provides none. | | `triggers.config_fields.required` | boolean | Whether a subscription must set this field to a non-empty value. A subscription request that omits a required field is refused. | | `triggers.default_interval_seconds` | integer | Polling interval, in seconds, a subscription uses when it does not set interval_seconds. Zero for a webhook trigger. | | `triggers.delivery_scope` | string (enum) | Whether this trigger delivers one event per connected account or one event for the whole connection, whatever accounts exist on it. Decides how the trigger is subscribed: an account-scoped trigger is subscribed on one connected account, a connection-scoped one on the connection itself, naming no account. One of: `ACCOUNT`, `CONNECTION`. | | `triggers.description` | string | What event the trigger reports. | | `triggers.display_name` | string | Human-readable name of the trigger. | | `triggers.min_interval_seconds` | integer | Shortest polling interval, in seconds, a subscription to this trigger may request. A lower interval_seconds is refused rather than rounded up. Zero for a webhook trigger, which has no interval. | | `triggers.mode` | string (enum) | How the trigger detects events. WEBHOOK - the provider sends each event to the connection's webhook URL, so the connection needs a webhook URL registered with the provider. POLL - Scalekit checks the provider on an interval, so a subscription can set interval_seconds. One of: `WEBHOOK`, `POLL`. | | `triggers.name` | string | Unique name of the trigger within its provider. Pass it as trigger_name when listing eligible accounts or creating a subscription. | | `triggers.platform_credentials` | string (enum) | Whether this trigger works on a connection that uses Scalekit's shared provider app. UNSUPPORTED means the connection needs your own provider app before this trigger can run; combine it with connection_uses_shared_app on the list response to decide whether to ask for one. UNSPECIFIED when the trigger declares a value this server does not recognise. One of: `SUPPORTED`, `UNSUPPORTED`. | | `triggers.provider` | string | The provider the trigger belongs to, the same as the connection's provider. | | `triggers.provides_bearer_secret` | boolean | Whether Scalekit generates this trigger's signing secret ([Generate a signing secret](https://docs.scalekit.com/agentkit/reference/triggers/generate-a-signing-secret/)) rather than you copying one from the provider's console. True only for providers that send no signature of their own. Always false for a polled trigger. | | `triggers.reason` | string | Why the trigger is unavailable on this connection. Populated whenever available is false. | | `triggers.refusal_code` | string (enum) | The stable code of the connection-level rule that made this trigger unavailable. SHARED_APP — the connection uses Scalekit's shared provider app. POLL_CONNECTION_SCOPE — a polled trigger cannot be connection-scoped. UNSPECIFIED when the trigger is available, or when a rule without a code refused it; reason and remedy are populated either way. One of: `SHARED_APP`, `POLL_CONNECTION_SCOPE`. | | `triggers.register_hint` | string | Provider-specific guidance on where to register the connection's ingress URL in the provider's console, written by the connector author. At most 500 characters, with no control characters. Empty when the connector provides none, and always empty for a polled trigger. | | `triggers.remedy` | string | What to do to make the trigger available. Populated whenever available is false. | | `triggers.required_scopes` | array of object | Permissions a connected account's grant must include to subscribe to this trigger. A subscription for an account whose grant lacks one of them is refused, naming the missing scope. | | `triggers.required_scopes.grant` | string | Which grant channel must carry the scope. Some providers fan events out differently for a bot install than for a user install, so this is not merely descriptive. | | `triggers.required_scopes.scope` | string | The provider's own name for the permission, exactly as it appears in their console. | | `triggers.requires_signing_secret` | boolean | Whether the provider signs this trigger's events with a secret you copy from the provider's console and sets on the connection's ingress endpoint. Always false for a polled trigger, which receives no inbound requests. | | `triggers.secret_hint` | string | Provider-specific guidance on where the provider shows the signing secret to copy, written by the connector author. At most 500 characters, with no control characters. Empty when the connector provides none, and always empty for a polled trigger. | **Errors** - `400`: Invalid request - connection_id is not a valid connection ID - `404`: Not found - the connection does not exist in this environment, or Triggers isn't turned on for your environment - `500`: Internal error - the triggers for this connection could not be read; retry the request **Request** ```bash curl -sS -X GET \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connections/conn_133400101014995480/triggers" \ -H "Authorization: Bearer $TOKEN" ``` **Response** ```json { "triggers": [ { "name": "salesforce_lead_created", "display_name": "Lead created", "description": "Fires when a new lead is created in Salesforce. Edits to an existing lead do not fire it.", "provider": "salesforce", "mode": "POLL", "required_scopes": [ { "scope": "api", "grant": "default" } ], "min_interval_seconds": 300, "default_interval_seconds": 300, "config_fields": [ { "key": "lead_source", "display_name": "Lead source", "help_text": "Only fire for leads from this source, e.g. Web. Leave blank for all leads.", "placeholder": "", "required": false } ], "available": true, "reason": "", "remedy": "", "delivery_scope": "ACCOUNT", "platform_credentials": "UNSUPPORTED", "refusal_code": "TRIGGER_REFUSAL_CODE_UNSPECIFIED", "requires_signing_secret": false, "register_hint": "", "secret_hint": "", "provides_bearer_secret": false } ], "connection_uses_shared_app": false } ``` **Python SDK:** `scalekit_client.actions.triggers.list_available` List every trigger a connection offers and whether each is usable there. ```python scalekit_client.actions.triggers.list_available( connection_id: str, ) -> ListAvailableTriggersResponse ``` Example: ```python result = scalekit_client.actions.triggers.list_available( connection_id="conn_133400101014995480", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | `str` | Yes | The connection (`conn_...`). | Returns `ListAvailableTriggersResponse`: All triggers (not paginated), plus `connection_uses_shared_app`. ## List accounts eligible for a trigger (beta) `GET /api/v1/connections/{connection_id}/triggers/{trigger_name}/eligible-accounts` This endpoint is in beta. Its request and response may change. Lists the connected accounts on a connection, and marks each one eligible or not for one trigger. An account that can't subscribe is still listed, with a `reason` and a `remedy`, such as reconnecting to grant a missing scope. Pass `query` to search account identifiers. Call this before you [subscribe accounts](https://docs.scalekit.com/agentkit/reference/triggers/batch-create-trigger-subscriptions/). **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | string | Yes | ID of the connection, in this environment. Up to 64 characters. | | `trigger_name` | string | Yes | Name of the trigger, as returned in the name field of [List available triggers](https://docs.scalekit.com/agentkit/reference/triggers/list-available-triggers/). Up to 128 characters. | **Query parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `page_size` | integer | No | How many accounts to return. Defaults to 10; a larger value than 100 is capped at 100 rather than refused. | | `page_token` | string | No | The next_page_token or prev_page_token from a previous response. Omit it to read the first page. A value that is not a token from a previous response is refused with 400. | | `query` | string | No | Narrows the list to accounts whose identifier contains this text, case-insensitively. Matched against every account on the connection, not only the current page. Up to 100 characters. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `accounts` | array of object | One page of the connection's connected accounts, eligible and ineligible, each with its health for this trigger. | | `accounts.eligible` | boolean | Whether this account can be subscribed to the trigger. When false, reason and remedy explain why and what to do. | | `accounts.health` | object | The account's own state, evaluated for what this trigger needs captured on the account: a webhook trigger needs the provider workspace, and an account-scoped webhook trigger also needs the provider user. A polled trigger needs neither, so the same account can be READY for one trigger and AWAITING_CONFIRMATION for another. Carried alongside eligible because they answer different questions: eligible is whether this trigger can be subscribed on this account, health is the account's own state and the single action that resolves it. | | `accounts.health.action` | string (enum) | The single action that resolves this state. Unspecified when there is nothing to do. One of: `RECONNECT`, `COMPLETE_SETUP`. | | `accounts.health.blocks_tool_calls` | boolean | Whether the account's credential itself is unusable, so tool calls made with it fail too. True only for NEEDS_REAUTHENTICATION. False for AWAITING_CONFIRMATION and WRONG_APP: the credential still works and tool calls still succeed, so an agent should keep using the account; only trigger events cannot reach it. | | `accounts.health.reason` | string | Why the account is in this state. Empty when the state is READY. | | `accounts.health.remedy` | string | What to do about it. Empty when the state is READY. | | `accounts.health.state` | string (enum) | READY — the account can receive triggers. AWAITING_CONFIRMATION — connected and working, but Scalekit has not confirmed which provider workspace or user it belongs to, so inbound events cannot be routed to it. WRONG_APP — authorized under a different OAuth app than the connection now uses. DISCONNECTED — the account was disconnected. NEEDS_REAUTHENTICATION — the credential itself is unusable, because it has expired or the account has not finished connecting. One of: `READY`, `AWAITING_CONFIRMATION`, `WRONG_APP`, `DISCONNECTED`, `NEEDS_REAUTHENTICATION`. | | `accounts.id` | string | ID of the connected account. Pass it as connected_account_id when creating a subscription. | | `accounts.identifier` | string | Your app's ID for the user, the value passed when the account was created. | | `accounts.reason` | string | Why this account cannot receive the trigger. Populated whenever eligible is false. | | `accounts.remedy` | string | What to do so the account can receive the trigger. Populated whenever eligible is false. | | `next_page_token` | string | Pass as page_token to read the next page. Empty when there is no next page. | | `prev_page_token` | string | Pass as page_token to read the previous page. Empty when there is no previous page. | | `total_size` | integer | How many of this connection's accounts match the query, across every page. Accounts on other connections are never counted. | **Errors** - `400`: Invalid request - connection_id is not a valid connection ID, page_token is not a token from a previous response, or query is longer than 100 characters - `404`: Not found - the connection does not exist in this environment, the connection's provider publishes no trigger with this name, or Triggers isn't turned on for your environment - `500`: Internal error - the accounts for this trigger could not be read; retry the request **Request** ```bash curl -sS -X GET \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connections/conn_133400101014995480/triggers/salesforce_lead_created/eligible-accounts" \ -H "Authorization: Bearer $TOKEN" ``` **Response** ```json { "accounts": [ { "id": "ca_133400349586228019", "identifier": "user_123", "eligible": true, "reason": "", "remedy": "", "health": { "state": "READY", "action": "ACCOUNT_HEALTH_ACTION_UNSPECIFIED", "blocks_tool_calls": false, "reason": "", "remedy": "" } } ], "next_page_token": "", "prev_page_token": "", "total_size": 1 } ``` **Python SDK:** `scalekit_client.actions.triggers.list_eligible_accounts` List a connection's connected accounts and whether each can subscribe. ```python scalekit_client.actions.triggers.list_eligible_accounts( connection_id: str, trigger_name: str, query: str | None = None, page_size: int | None = None, page_token: str | None = None, ) -> ListEligibleAccountsResponse ``` Example: ```python result = scalekit_client.actions.triggers.list_eligible_accounts( connection_id="conn_133400101014995480", trigger_name="salesforce_lead_created", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | `str` | Yes | The connection (`conn_...`). | | `trigger_name` | `str` | Yes | `AvailableTrigger.name`. | | `query` | `str \| None` | No | Filter accounts by identifier. | | `page_size` | `int \| None` | No | Accounts per page; the server default is 10 and values above 100 are capped. | | `page_token` | `str \| None` | No | `next_page_token` or `prev_page_token` of a previous page. | Returns `ListEligibleAccountsResponse`: One page of accounts with their eligibility and health. ## List trigger subscriptions (beta) `GET /api/v1/connections/{connection_id}/trigger-subscriptions` This endpoint is in beta. Its request and response may change. Lists the trigger subscriptions on a connection, newest first. Filter by `trigger_name` and `statuses`. A polling subscription also returns when it last ran and why its last run failed. `status_counts` counts the subscriptions in each status across every page. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | string | Yes | ID of the connection, in this environment. Up to 64 characters. | **Query parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `page_size` | integer | No | How many subscriptions to return. Defaults to 10; a larger value than 100 is capped at 100 rather than refused. | | `page_token` | string | No | The next_page_token or prev_page_token from a previous response. Omit it to read the first page. A value that is not a token from a previous response is refused with 400. | | `statuses` | array of string (enum) | No | Narrows the list to subscriptions in these statuses, at most 4. Omit it for every status; TRIGGER_SUBSCRIPTION_STATUS_UNSPECIFIED is refused. Does not affect status_counts, so a client filtering to one status still sees the totals for all of them. - ACTIVE: Delivering. For a polled trigger, at the requested interval. - THROTTLED: Running slower than requested, not stopped. Still delivering. - PAUSED: Stopped by you. - PAUSED_CREDENTIAL: Stopped because the account's credential no longer works. Distinct from PAUSED so the two have distinguishable explanations for missing events. | | `trigger_name` | string | No | Narrows the list to one trigger's subscriptions. Omit it to list every subscription on the connection. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `next_page_token` | string | Pass as page_token to read the next page. Empty when there is no next page. | | `prev_page_token` | string | Pass as page_token to read the previous page. Empty when there is no previous page. | | `status_counts` | object | How many subscriptions sit in each status, keyed by the status name. Counted over the whole matching set rather than the current page, so a client can show per-status totals without fetching every page. Honours trigger_name but ignores statuses, so the totals stay still while you switch between status filters. | | `subscriptions` | array of object | One page of the connection's trigger subscriptions that match the filters, newest first. | | `subscriptions.account_health` | string (enum) | How the subscribed account stands for this trigger. AWAITING_CONFIRMATION means Scalekit cannot route this trigger's events to the account yet, so the subscription will not fire until the account's user reconnects it. Absent for connection-scoped subscriptions. One of: `READY`, `AWAITING_CONFIRMATION`, `WRONG_APP`, `DISCONNECTED`, `NEEDS_REAUTHENTICATION`. | | `subscriptions.config` | object | The subscription's filter values, keyed by the trigger's config_fields keys. Keys are returned exactly as stored; send them back without changing their case. | | `subscriptions.connected_account_id` | string | ID of the subscribed connected account. Empty for a connection-scoped subscription. | | `subscriptions.connected_account_identifier` | string | The identifier the connected account was created with, your app's ID for the user, such as `user_123`, so you can label the subscription without fetching each account. Empty for a connection-scoped subscription, and empty when the account has no identifier recorded — fall back to connected_account_id rather than showing a blank. | | `subscriptions.connection_id` | string | ID of the connection the subscription belongs to. | | `subscriptions.created_at` | string | When the subscription was created. Returned so a list can be ordered or explained by age without a second lookup. | | `subscriptions.delivery_scope` | string (enum) | Whether this subscription receives one event per connected account or one event for the whole connection. A connection-scoped subscription carries no connected_account_id — the connection is the only recipient. One of: `ACCOUNT`, `CONNECTION`. | | `subscriptions.effective_interval_seconds` | integer | The polling interval, in seconds, the subscription actually runs at. Larger than requested_interval_seconds when the environment's polling budget is shared across many polled subscriptions; the subscription's status is then THROTTLED. Zero for a webhook trigger. | | `subscriptions.id` | string | Unique identifier of the trigger subscription. | | `subscriptions.poll_state` | object | How a POLL subscription's feed is doing: when it last ran, why the last run failed, and whether polling is held off. Absent for a WEBHOOK subscription, which is never polled. | | `subscriptions.poll_state.backoff_until` | string | Polling is held off until this time, because the provider asked Scalekit to slow down or a run failed in a way retrying cannot fix. Absent when no hold-off has been recorded. A time in the past means the hold-off has ended and the next scheduled run polls again. | | `subscriptions.poll_state.consecutive_failures` | integer | How many runs in a row have failed. Zero after any successful run. | | `subscriptions.poll_state.last_error` | string | A short, human-readable explanation of why the most recent run failed and what happens next. Empty when the most recent run succeeded or no run has happened yet. Chosen from a fixed set of messages by last_error_code and never contains the provider's response. The wording can change: match on last_error_code, not on this text. | | `subscriptions.poll_state.last_error_code` | string (enum) | Why the most recent run failed, as a stable code to branch on. TRIGGER_POLL_ERROR_CODE_UNSPECIFIED when the most recent run succeeded or no run has happened yet. CREDENTIAL_REVOKED and INVALID_CONFIGURATION need a change from you before polling can succeed; every other code is retried automatically. One of: `RATE_LIMITED`, `CREDENTIAL_REVOKED`, `INVALID_CONFIGURATION`, `PROVIDER_ERROR`, `DELIVERY_FAILED`, `TIMED_OUT`, `POLL_FAILED`. | | `subscriptions.poll_state.last_run_at` | string | When Scalekit last polled the provider for this subscription, whether or not the run succeeded. Absent until the first run. A value far older than the effective interval means the feed has stopped running. | | `subscriptions.poll_state.watermark_updated_at` | string | When the feed's position last moved forward — the last time a run found and delivered newer events. A run that finds nothing new leaves it unchanged. | | `subscriptions.provider` | string | The provider the trigger belongs to. | | `subscriptions.requested_interval_seconds` | integer | The polling interval, in seconds, the subscription asked for, or the trigger's default when it did not set one. Returned next to effective_interval_seconds so a subscription running slower than requested is visible. Zero for a webhook trigger. | | `subscriptions.status` | string (enum) | Whether the subscription is delivering. ACTIVE — delivering. THROTTLED — delivering, but polling slower than requested; compare requested_interval_seconds with effective_interval_seconds. PAUSED — stopped by you. PAUSED_CREDENTIAL — stopped because the account's credential no longer works. One of: `ACTIVE`, `THROTTLED`, `PAUSED`, `PAUSED_CREDENTIAL`. | | `subscriptions.trigger_name` | string | Name of the subscribed trigger, as returned by [List available triggers](https://docs.scalekit.com/agentkit/reference/triggers/list-available-triggers/). | | `total_size` | integer | How many subscriptions match the filters in total, across every page. | **Errors** - `400`: Invalid request - connection_id is not a valid connection ID, page_token is not a token from a previous response, or statuses holds TRIGGER_SUBSCRIPTION_STATUS_UNSPECIFIED or an unknown status - `404`: Not found - the connection does not exist in this environment, or Triggers isn't turned on for your environment - `500`: Internal error - the subscriptions could not be read; retry the request **Request** ```bash curl -sS -G -X GET \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connections/conn_133400101014995480/trigger-subscriptions" \ -H "Authorization: Bearer $TOKEN" \ --data-urlencode "trigger_name=salesforce_lead_created" ``` **Response** ```json { "subscriptions": [ { "id": "trgs_141020933185734981", "connected_account_id": "ca_133400349586228019", "connected_account_identifier": "user_123", "connection_id": "conn_133400101014995480", "provider": "salesforce", "trigger_name": "salesforce_lead_created", "status": "ACTIVE", "delivery_scope": "ACCOUNT", "requested_interval_seconds": 300, "effective_interval_seconds": 300, "config": { "lead_source": "Web" }, "created_at": "2026-10-28T09:12:44.120Z", "poll_state": { "last_run_at": "2026-10-28T09:17:44.310Z", "last_error": "", "last_error_code": "TRIGGER_POLL_ERROR_CODE_UNSPECIFIED", "consecutive_failures": 0, "watermark_updated_at": "2026-10-28T09:17:44.310Z" }, "account_health": "READY" } ], "next_page_token": "", "prev_page_token": "", "total_size": 1, "status_counts": { "ACTIVE": 1 } } ``` **Python SDK:** `scalekit_client.actions.triggers.list_subscriptions` List a connection's trigger subscriptions, one page at a time. ```python scalekit_client.actions.triggers.list_subscriptions( connection_id: str, trigger_name: str | None = None, statuses: Sequence[TriggerSubscriptionStatus | str | int] | None = None, page_size: int | None = None, page_token: str | None = None, ) -> ListTriggerSubscriptionsResponse ``` Example: ```python result = scalekit_client.actions.triggers.list_subscriptions( connection_id="conn_133400101014995480", trigger_name="salesforce_lead_created", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | `str` | Yes | The connection (`conn_...`). | | `trigger_name` | `str \| None` | No | Only subscriptions to this trigger. | | `statuses` | `Sequence[TriggerSubscriptionStatus \| str \| int] \| None` | No | Only subscriptions in these states: `TriggerSubscriptionStatus` members, their names, or raw numbers. | | `page_size` | `int \| None` | No | Subscriptions per page; the server caps it at 100. | | `page_token` | `str \| None` | No | `next_page_token` or `prev_page_token` of a previous page. | Returns `ListTriggerSubscriptionsResponse`: One page of subscriptions, `total_size` and `status_counts`. ## Subscribe to a trigger (beta) `POST /api/v1/connections/{connection_id}/trigger-subscriptions` This endpoint is in beta. Its request and response may change. Subscribes one connected account, or the whole connection, to a trigger. Pass `connected_account_id` for a per-account trigger, and leave it out for a per-connection trigger. Set the trigger's filters in `config`, using the keys from the trigger's `config_fields`. For a polling trigger, `interval_seconds` sets how often Scalekit checks the app; it defaults to the trigger's default interval, and a value below its minimum is refused. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | string | Yes | ID of the connection, in this environment. Up to 64 characters. | **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `trigger_name` | string | Yes | Name of the trigger to subscribe to, as returned in the name field of [List available triggers](https://docs.scalekit.com/agentkit/reference/triggers/list-available-triggers/). Up to 128 characters. | | `config` | object | No | Filter values for the subscription, keyed by the trigger's config_fields keys, spelled exactly as returned. Every required field must be set. An unknown key, or a value longer than 512 characters, is refused. Subscribing again with different values creates another subscription; subscribing again with identical values returns 409. | | `connected_account_id` | string | No | Connected account to subscribe on, for an account-scoped trigger. Omit it for a connection-scoped trigger — the trigger is then subscribed on the connection itself and no account is named. Whether omitting it is valid depends on the trigger's own delivery_scope. | | `interval_seconds` | integer | No | Polling interval in seconds, for a polled trigger only. Omit it to use the trigger's default_interval_seconds. A value below the trigger's min_interval_seconds is refused, naming the minimum, rather than rounded up. Setting it for a webhook trigger is refused. The subscription can run slower than requested when the environment's polling budget is shared across many polled subscriptions; effective_interval_seconds on the response shows the interval it runs at. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `account_health` | string (enum) | How the subscribed account stands for this trigger. AWAITING_CONFIRMATION means Scalekit cannot route this trigger's events to the account yet, so the subscription will not fire until the account's user reconnects it. Absent for connection-scoped subscriptions. One of: `READY`, `AWAITING_CONFIRMATION`, `WRONG_APP`, `DISCONNECTED`, `NEEDS_REAUTHENTICATION`. | | `config` | object | The subscription's filter values, keyed by the trigger's config_fields keys. Keys are returned exactly as stored; send them back without changing their case. | | `connected_account_id` | string | ID of the subscribed connected account. Empty for a connection-scoped subscription. | | `connected_account_identifier` | string | The identifier the connected account was created with, your app's ID for the user, such as `user_123`, so you can label the subscription without fetching each account. Empty for a connection-scoped subscription, and empty when the account has no identifier recorded — fall back to connected_account_id rather than showing a blank. | | `connection_id` | string | ID of the connection the subscription belongs to. | | `created_at` | string | When the subscription was created. Returned so a list can be ordered or explained by age without a second lookup. | | `delivery_scope` | string (enum) | Whether this subscription receives one event per connected account or one event for the whole connection. A connection-scoped subscription carries no connected_account_id — the connection is the only recipient. One of: `ACCOUNT`, `CONNECTION`. | | `effective_interval_seconds` | integer | The polling interval, in seconds, the subscription actually runs at. Larger than requested_interval_seconds when the environment's polling budget is shared across many polled subscriptions; the subscription's status is then THROTTLED. Zero for a webhook trigger. | | `id` | string | Unique identifier of the trigger subscription. | | `poll_state` | object | How a POLL subscription's feed is doing: when it last ran, why the last run failed, and whether polling is held off. Absent for a WEBHOOK subscription, which is never polled. | | `poll_state.backoff_until` | string | Polling is held off until this time, because the provider asked Scalekit to slow down or a run failed in a way retrying cannot fix. Absent when no hold-off has been recorded. A time in the past means the hold-off has ended and the next scheduled run polls again. | | `poll_state.consecutive_failures` | integer | How many runs in a row have failed. Zero after any successful run. | | `poll_state.last_error` | string | A short, human-readable explanation of why the most recent run failed and what happens next. Empty when the most recent run succeeded or no run has happened yet. Chosen from a fixed set of messages by last_error_code and never contains the provider's response. The wording can change: match on last_error_code, not on this text. | | `poll_state.last_error_code` | string (enum) | Why the most recent run failed, as a stable code to branch on. TRIGGER_POLL_ERROR_CODE_UNSPECIFIED when the most recent run succeeded or no run has happened yet. CREDENTIAL_REVOKED and INVALID_CONFIGURATION need a change from you before polling can succeed; every other code is retried automatically. One of: `RATE_LIMITED`, `CREDENTIAL_REVOKED`, `INVALID_CONFIGURATION`, `PROVIDER_ERROR`, `DELIVERY_FAILED`, `TIMED_OUT`, `POLL_FAILED`. | | `poll_state.last_run_at` | string | When Scalekit last polled the provider for this subscription, whether or not the run succeeded. Absent until the first run. A value far older than the effective interval means the feed has stopped running. | | `poll_state.watermark_updated_at` | string | When the feed's position last moved forward — the last time a run found and delivered newer events. A run that finds nothing new leaves it unchanged. | | `provider` | string | The provider the trigger belongs to. | | `requested_interval_seconds` | integer | The polling interval, in seconds, the subscription asked for, or the trigger's default when it did not set one. Returned next to effective_interval_seconds so a subscription running slower than requested is visible. Zero for a webhook trigger. | | `status` | string (enum) | Whether the subscription is delivering. ACTIVE — delivering. THROTTLED — delivering, but polling slower than requested; compare requested_interval_seconds with effective_interval_seconds. PAUSED — stopped by you. PAUSED_CREDENTIAL — stopped because the account's credential no longer works. One of: `ACTIVE`, `THROTTLED`, `PAUSED`, `PAUSED_CREDENTIAL`. | | `trigger_name` | string | Name of the subscribed trigger, as returned by [List available triggers](https://docs.scalekit.com/agentkit/reference/triggers/list-available-triggers/). | **Errors** - `400`: Invalid request - the message names what to change. Returned when an ID is malformed; config holds an unknown key, misses a required key, or holds a value longer than 512 characters; interval_seconds is below the trigger's minimum, or is set for a webhook trigger; connected_account_id is missing for an account-scoped trigger, or present for a connection-scoped one; the trigger polls and is connection-scoped, which is not supported; or the account cannot receive the trigger because its grant lacks a required scope or the bot install the trigger needs, it was authorized under a different app, the trigger needs your own provider app, its provider workspace or user is not yet confirmed, or it holds no usable credential - `404`: Not found - the connection or connected account does not exist in this environment, the connected account belongs to a different connection, the connection's provider publishes no trigger with this name, or Triggers isn't turned on for your environment - `409`: Conflict - this account, or this connection for a connection-scoped trigger, already has a subscription to this trigger with exactly these config values - `500`: Internal error - the subscription could not be created; nothing was created - `503`: Service unavailable - polling for the new subscription could not be scheduled, so the subscription was not created; retry the same request **Request** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connections/conn_133400101014995480/trigger-subscriptions" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "trigger_name": "salesforce_lead_created", "connected_account_id": "ca_133400349586228019", "config": { "lead_source": "Web" }, "interval_seconds": 300 }' ``` **Response** ```json { "id": "trgs_141020933185734981", "connected_account_id": "ca_133400349586228019", "connected_account_identifier": "user_123", "connection_id": "conn_133400101014995480", "provider": "salesforce", "trigger_name": "salesforce_lead_created", "status": "ACTIVE", "delivery_scope": "ACCOUNT", "requested_interval_seconds": 300, "effective_interval_seconds": 300, "config": { "lead_source": "Web" }, "created_at": "2026-10-28T09:12:44.120Z", "poll_state": { "last_error": "", "last_error_code": "TRIGGER_POLL_ERROR_CODE_UNSPECIFIED", "consecutive_failures": 0, "watermark_updated_at": "2026-10-28T09:12:44.120Z" }, "account_health": "READY" } ``` **Python SDK:** `scalekit_client.actions.triggers.create_subscription` Subscribe a connected account, or the whole connection, to a trigger. Not retried on `UNAVAILABLE`: a retry could create the subscription twice. Creating the same subscription again raises `ScalekitConflictException`. ```python scalekit_client.actions.triggers.create_subscription( connection_id: str, trigger_name: str, connected_account_id: str | None = None, interval: timedelta | None = None, config: Mapping[str, str] | None = None, ) -> TriggerSubscription ``` Example: ```python from datetime import timedelta result = scalekit_client.actions.triggers.create_subscription( connection_id="conn_133400101014995480", trigger_name="salesforce_lead_created", connected_account_id="ca_133400349586228019", interval=timedelta(minutes=5), config={"lead_source": "Web"}, ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | `str` | Yes | The connection (`conn_...`). | | `trigger_name` | `str` | Yes | `AvailableTrigger.name`. | | `connected_account_id` | `str \| None` | No | The account (`ca_...`). Leave out only for a connection-scoped trigger. | | `interval` | `timedelta \| None` | No | Poll interval for a poll trigger, in whole seconds; at least the trigger's `min_interval`. Leave out for the default and for webhook triggers. | | `config` | `Mapping[str, str] \| None` | No | Values for the trigger's `config_fields`. | Returns `TriggerSubscription`: The new subscription. ## Subscribe several accounts to a trigger (beta) `POST /api/v1/connections/{connection_id}/trigger-subscriptions:batchCreate` This endpoint is in beta. Its request and response may change. Subscribes up to 100 connected accounts to one trigger with the same filters, and returns a result for each account. An account that can't subscribe doesn't stop the others: its result is `REFUSED`, `NOT_FOUND` or `FAILED`, with a `reason`. Retrying the request is safe; an account subscribed earlier comes back as `ALREADY_SUBSCRIBED`. For a per-connection trigger, send an empty `connected_account_ids`. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | string | Yes | ID of the connection, in this environment. Up to 64 characters. | **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `trigger_name` | string | Yes | Name of the trigger to subscribe to, as returned in the name field of [List available triggers](https://docs.scalekit.com/agentkit/reference/triggers/list-available-triggers/). Up to 128 characters. | | `config` | object | No | Filter values applied to every account in the batch, keyed by the trigger's config_fields keys. Each account gets its own subscription with these values, so subscribing the same accounts again with different values adds subscriptions rather than replacing these. Invalid values refuse the whole request. | | `connected_account_ids` | array of string | No | Connected accounts to subscribe, at most 100 — one page of the account picker — with no repeats. Which to send depends on the trigger's delivery_scope, from [List available triggers](https://docs.scalekit.com/agentkit/reference/triggers/list-available-triggers/). For an ACCOUNT-scoped trigger, list at least one account; an empty list refuses the whole request with a 400. For a CONNECTION-scoped trigger, send an empty list: the trigger is subscribed once, on the connection itself, and results holds exactly one result, with an empty connected_account_id. Naming accounts for a connection-scoped trigger refuses each of them (REFUSED), since that trigger is not subscribed per account. | | `interval_seconds` | integer | No | Polling interval in seconds, for a polled trigger only, applied to every account in the batch. Omit it to use the trigger's default_interval_seconds. A value below the trigger's min_interval_seconds, or any value for a webhook trigger, refuses the whole request. A large batch of polled subscriptions shares the environment's polling budget, so the subscriptions can run slower than requested; effective_interval_seconds on each subscription shows the interval it runs at. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `created_count` | integer | How many subscriptions this call created. Excludes accounts that were already subscribed. | | `results` | array of object | One result per requested account, in the order the accounts were sent, so a client can pair them up positionally without matching on id. For a connection-scoped trigger subscribed with an empty connected_account_ids, exactly one result, with an empty connected_account_id. | | `results.connected_account_id` | string | The connected account ID this result is for, as sent in the request. | | `results.outcome` | string (enum) | What happened to this account. CREATED — a subscription was created. ALREADY_SUBSCRIBED — the account already had a subscription to this trigger with these exact config values; nothing new was created, and list subscriptions to read the existing one. REFUSED — the account cannot receive this trigger; reason says why and what to do. NOT_FOUND — no such account on this connection in this environment. FAILED — the attempt failed for a reason unrelated to the account; nothing was created, and retrying may succeed. One of: `CREATED`, `ALREADY_SUBSCRIBED`, `REFUSED`, `NOT_FOUND`, `FAILED`. | | `results.reason` | string | Why the account was not subscribed, and for a refusal what to do about it, in one sentence — the same text [Subscribe to a trigger](https://docs.scalekit.com/agentkit/reference/triggers/create-a-trigger-subscription/) returns for the same account. Empty for CREATED. | | `results.subscription` | object | The created subscription, for CREATED. Absent for every other outcome, including ALREADY_SUBSCRIBED. | | `results.subscription.account_health` | string (enum) | How the subscribed account stands for this trigger. AWAITING_CONFIRMATION means Scalekit cannot route this trigger's events to the account yet, so the subscription will not fire until the account's user reconnects it. Absent for connection-scoped subscriptions. One of: `READY`, `AWAITING_CONFIRMATION`, `WRONG_APP`, `DISCONNECTED`, `NEEDS_REAUTHENTICATION`. | | `results.subscription.config` | object | The subscription's filter values, keyed by the trigger's config_fields keys. Keys are returned exactly as stored; send them back without changing their case. | | `results.subscription.connected_account_id` | string | ID of the subscribed connected account. Empty for a connection-scoped subscription. | | `results.subscription.connected_account_identifier` | string | The identifier the connected account was created with, your app's ID for the user, such as `user_123`, so you can label the subscription without fetching each account. Empty for a connection-scoped subscription, and empty when the account has no identifier recorded — fall back to connected_account_id rather than showing a blank. | | `results.subscription.connection_id` | string | ID of the connection the subscription belongs to. | | `results.subscription.created_at` | string | When the subscription was created. Returned so a list can be ordered or explained by age without a second lookup. | | `results.subscription.delivery_scope` | string (enum) | Whether this subscription receives one event per connected account or one event for the whole connection. A connection-scoped subscription carries no connected_account_id — the connection is the only recipient. One of: `ACCOUNT`, `CONNECTION`. | | `results.subscription.effective_interval_seconds` | integer | The polling interval, in seconds, the subscription actually runs at. Larger than requested_interval_seconds when the environment's polling budget is shared across many polled subscriptions; the subscription's status is then THROTTLED. Zero for a webhook trigger. | | `results.subscription.id` | string | Unique identifier of the trigger subscription. | | `results.subscription.poll_state` | object | How a POLL subscription's feed is doing: when it last ran, why the last run failed, and whether polling is held off. Absent for a WEBHOOK subscription, which is never polled. | | `results.subscription.provider` | string | The provider the trigger belongs to. | | `results.subscription.requested_interval_seconds` | integer | The polling interval, in seconds, the subscription asked for, or the trigger's default when it did not set one. Returned next to effective_interval_seconds so a subscription running slower than requested is visible. Zero for a webhook trigger. | | `results.subscription.status` | string (enum) | Whether the subscription is delivering. ACTIVE — delivering. THROTTLED — delivering, but polling slower than requested; compare requested_interval_seconds with effective_interval_seconds. PAUSED — stopped by you. PAUSED_CREDENTIAL — stopped because the account's credential no longer works. One of: `ACTIVE`, `THROTTLED`, `PAUSED`, `PAUSED_CREDENTIAL`. | | `results.subscription.trigger_name` | string | Name of the subscribed trigger, as returned by [List available triggers](https://docs.scalekit.com/agentkit/reference/triggers/list-available-triggers/). | **Errors** - `400`: Invalid request, nothing created - connection_id or an entry of connected_account_ids is malformed; config is invalid for this trigger; interval_seconds is below the trigger's minimum, or is set for a webhook trigger; the trigger polls and is connection-scoped, which is not supported; or connected_account_ids is empty for a trigger that is subscribed per connected account. A well-formed ID naming an account that does not exist is not this case: it comes back as a per-account NOT_FOUND result with a 200, so one bad ID in a batch of a hundred does not discard the ninety-nine that would have succeeded - `404`: Not found - the connection does not exist in this environment, the connection's provider publishes no trigger with this name, or Triggers isn't turned on for your environment - `500`: Internal error - the connection or the trigger's definition could not be read, so no account was processed; retry the request **Request** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connections/conn_133400101014995480/trigger-subscriptions:batchCreate" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "trigger_name": "salesforce_lead_created", "connected_account_ids": [ "ca_133400349586228019", "ca_133400349586231107" ], "config": { "lead_source": "Web" }, "interval_seconds": 300 }' ``` **Response** ```json { "results": [ { "connected_account_id": "ca_133400349586228019", "outcome": "ALREADY_SUBSCRIBED", "reason": "this principal already has a subscription to this trigger with these exact settings" }, { "connected_account_id": "ca_133400349586231107", "outcome": "CREATED", "reason": "", "subscription": { "id": "trgs_141020933185736602", "connected_account_id": "ca_133400349586231107", "connected_account_identifier": "user_456", "connection_id": "conn_133400101014995480", "provider": "salesforce", "trigger_name": "salesforce_lead_created", "status": "ACTIVE", "delivery_scope": "ACCOUNT", "requested_interval_seconds": 300, "effective_interval_seconds": 300, "config": { "lead_source": "Web" }, "created_at": "2026-10-28T09:20:02.480Z", "poll_state": { "last_error": "", "last_error_code": "TRIGGER_POLL_ERROR_CODE_UNSPECIFIED", "consecutive_failures": 0, "watermark_updated_at": "2026-10-28T09:20:02.480Z" }, "account_health": "READY" } } ], "created_count": 1 } ``` **Python SDK:** `scalekit_client.actions.triggers.batch_create_subscriptions` Subscribe up to 100 connected accounts to a trigger in one call. Not atomic: each account gets its own outcome in `results` (`CREATED`, `ALREADY_SUBSCRIBED`, `REFUSED`, `NOT_FOUND`, `FAILED`), and those never raise. Safe to call again for the `FAILED` ones. For a connection-scoped trigger, leave `connected_account_ids` out. ```python scalekit_client.actions.triggers.batch_create_subscriptions( connection_id: str, trigger_name: str, connected_account_ids: Sequence[str] | None = None, interval: timedelta | None = None, config: Mapping[str, str] | None = None, ) -> BatchCreateTriggerSubscriptionsResponse ``` Example: ```python from datetime import timedelta result = scalekit_client.actions.triggers.batch_create_subscriptions( connection_id="conn_133400101014995480", trigger_name="salesforce_lead_created", connected_account_ids=["ca_133400349586228019", "ca_133400349586231107"], interval=timedelta(minutes=5), config={"lead_source": "Web"}, ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | `str` | Yes | The connection (`conn_...`). | | `trigger_name` | `str` | Yes | `AvailableTrigger.name`. | | `connected_account_ids` | `Sequence[str] \| None` | No | The accounts (`ca_...`), unique. | | `interval` | `timedelta \| None` | No | Poll interval, in whole seconds (see `create_subscription`). | | `config` | `Mapping[str, str] \| None` | No | Values for the trigger's `config_fields`. | Returns `BatchCreateTriggerSubscriptionsResponse`: Per-account results and `created_count`. ## Delete a trigger subscription (beta) `DELETE /api/v1/connections/{connection_id}/trigger-subscriptions/{subscription_id}` This endpoint is in beta. Its request and response may change. Deletes a trigger subscription, so the account or connection stops receiving the trigger. A polling subscription stops polling. An event already on its way to your endpoint is still delivered. To receive the trigger again, create a new subscription. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | string | Yes | ID of the connection, in this environment. Up to 64 characters. | | `subscription_id` | string | Yes | ID of the trigger subscription to delete. It must belong to the connection named in the path. | **Errors** - `400`: Invalid request - connection_id is not a valid connection ID, or subscription_id is not a valid trigger subscription ID - `404`: Not found - no subscription with this ID exists on this connection in this environment, or Triggers isn't turned on for your environment - `500`: Internal error - the subscription could not be deleted; retry the request **Request** ```bash curl -sS -X DELETE \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connections/conn_133400101014995480/trigger-subscriptions/trgs_141020933185734981" \ -H "Authorization: Bearer $TOKEN" ``` **Response** ```json {} ``` **Python SDK:** `scalekit_client.actions.triggers.delete_subscription` Delete one trigger subscription. Retried on `UNAVAILABLE`, so a `ScalekitNotFoundException` can also mean an earlier attempt of this same call already deleted it. ```python scalekit_client.actions.triggers.delete_subscription( connection_id: str, subscription_id: str, ) -> None ``` Example: ```python result = scalekit_client.actions.triggers.delete_subscription( connection_id="conn_133400101014995480", subscription_id="trgs_141020933185734981", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | `str` | Yes | The connection (`conn_...`). | | `subscription_id` | `str` | Yes | The subscription (`trgs_...`). | Returns `None`. ## Delete several trigger subscriptions (beta) `POST /api/v1/connections/{connection_id}/trigger-subscriptions:batchDelete` This endpoint is in beta. Its request and response may change. Deletes trigger subscriptions on one connection and returns a result for each. Name them in one of two ways: by `subscription_ids`, or by `trigger_name` together with `connected_account_ids`, which deletes every subscription those accounts have to that trigger. A subscription that's already gone, such as one you deleted earlier, comes back as `BATCH_DELETE_OUTCOME_NOT_FOUND`, so retrying the request is safe. Up to 60 requests a minute per environment. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | string | Yes | ID of the connection whose subscriptions to delete. Only subscriptions on this connection are deleted: a subscription ID from another connection comes back as NOT_FOUND. | **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connected_account_ids` | array of string | No | With trigger_name, the other selector: connected account IDs (`ca_`) whose subscriptions to trigger_name to delete, at most 100, with no repeats. One account can hold several subscriptions to one trigger, one per filter; each is deleted and gets its own result. Results are grouped by account in the order sent and, within an account, ordered by ascending subscription ID. An account with no matching subscription gets one NOT_FOUND result with an empty subscription_id. A connection-scoped subscription names no account, so it is never matched here: delete it by subscription_ids. | | `subscription_ids` | array of string | No | Trigger subscription IDs (`trgs_`) to delete, at most 100, with no repeats. One of the two selectors: send either this, or trigger_name with connected_account_ids, never both. Results come back one per ID, in the order sent. Works for account-scoped and connection-scoped subscriptions alike. | | `trigger_name` | string | No | With connected_account_ids, the other selector: deletes every subscription the listed accounts hold to this trigger on this connection, whatever its filter. Must be sent together with connected_account_ids; sent alone it is refused, so no request can delete every subscription to a trigger. Not checked against the trigger catalogue: a name no subscription uses gives NOT_FOUND for every account. At most 128 characters. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `deleted_count` | integer | How many subscriptions this call deleted. Excludes NOT_FOUND results, so a subscription an earlier attempt already deleted is not counted again. | | `results` | array of object | For subscription_ids, one result per ID in the order sent, so a client can pair them up positionally. For trigger_name with connected_account_ids, the results are grouped by account in the order sent, ordered by ascending subscription ID within an account, with one NOT_FOUND result for an account that held no matching subscription. | | `results.connected_account_id` | string | For trigger_name with connected_account_ids, the account this result belongs to, as sent. Always empty for a subscription_ids request. | | `results.outcome` | string (enum) | What happened to this subscription. BATCH_DELETE_OUTCOME_DELETED — deleted by this call. BATCH_DELETE_OUTCOME_NOT_FOUND — no such subscription on this connection in this environment, including one an earlier attempt already deleted; treat it like DELETED when retrying. BATCH_DELETE_OUTCOME_FAILED — the delete could not be completed and may or may not have happened; retrying is safe. BATCH_DELETE_OUTCOME_NOT_ATTEMPTED — the request ran out of time before reaching this subscription; nothing was changed for it, so send it again. One of: `BATCH_DELETE_OUTCOME_DELETED`, `BATCH_DELETE_OUTCOME_NOT_FOUND`, `BATCH_DELETE_OUTCOME_FAILED`, `BATCH_DELETE_OUTCOME_NOT_ATTEMPTED`. | | `results.reason` | string | One sentence explaining the outcome. Empty for BATCH_DELETE_OUTCOME_DELETED. Never contains internal error detail. The wording can change: match on outcome, not on this text. | | `results.subscription_id` | string | The subscription this result is about. For subscription_ids, the ID exactly as sent. For trigger_name with connected_account_ids, a subscription found for the account; empty only on the single NOT_FOUND result of an account that held no matching subscription. | **Errors** - `400`: Invalid request, nothing deleted - both selectors or neither were sent, or `trigger_name` and `connected_account_ids` were not sent together; a list holds more than 100 IDs or repeats one, including two spellings of the same ID; `connection_id` is not a valid connection ID, an entry of `subscription_ids` is not a trigger subscription ID (`trgs_`), or an entry of `connected_account_ids` is not a connected account ID (`ca_`); or the named accounts hold more than 1000 subscriptions to the trigger, so fewer `connected_account_ids` must be sent per request. A well-formed ID naming a subscription that does not exist, was already deleted, or belongs to another connection is not this case: it comes back as a NOT_FOUND result with a 200, so one stale ID does not discard the rest of the batch. - `404`: Not found - Triggers isn't turned on for your environment, or `connection_id` can never name a connection. Nothing was deleted. A missing subscription is not this case: it is a NOT_FOUND result with a 200. - `429`: Too many requests - this environment has sent too many batch delete requests in the last minute. Nothing was deleted; retry after a short wait. - `500`: Internal error - the subscriptions that `trigger_name` and `connected_account_ids` name could not be read, so nothing was deleted. Only that selector fails this way; retrying is safe. **Request** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connections/conn_133400101014995480/trigger-subscriptions:batchDelete" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "subscription_ids": [ "trgs_141020933185734981", "trgs_141020933185736602" ] }' ``` **Response** ```json { "results": [ { "subscription_id": "trgs_141020933185734981", "connected_account_id": "", "outcome": "BATCH_DELETE_OUTCOME_NOT_FOUND", "reason": "trigger subscription not found" }, { "subscription_id": "trgs_141020933185736602", "connected_account_id": "", "outcome": "BATCH_DELETE_OUTCOME_DELETED", "reason": "" } ], "deleted_count": 1 } ``` **Python SDK:** `scalekit_client.actions.triggers.batch_delete_subscriptions` Delete many trigger subscriptions in one call. Choose one selector: `subscription_ids` (up to 100), or `trigger_name` with `connected_account_ids` (up to 100). Each subscription gets its own outcome in `results` (`DELETED`, `NOT_FOUND`, `FAILED`, `NOT_ATTEMPTED`), and those never raise. Safe to call again. ```python scalekit_client.actions.triggers.batch_delete_subscriptions( connection_id: str, subscription_ids: Sequence[str] | None = None, trigger_name: str | None = None, connected_account_ids: Sequence[str] | None = None, ) -> BatchDeleteTriggerSubscriptionsResponse ``` Example: ```python result = scalekit_client.actions.triggers.batch_delete_subscriptions( connection_id="conn_133400101014995480", subscription_ids=["trgs_141020933185734981", "trgs_141020933185736602"], ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | `str` | Yes | The connection (`conn_...`). | | `subscription_ids` | `Sequence[str] \| None` | No | The subscriptions to delete (`trgs_...`). | | `trigger_name` | `str \| None` | No | With `connected_account_ids`: the trigger to unsubscribe. | | `connected_account_ids` | `Sequence[str] \| None` | No | With `trigger_name`: the accounts (`ca_...`). | Returns `BatchDeleteTriggerSubscriptionsResponse`: Per-subscription results and `deleted_count`. ## Create a webhook URL (beta) `POST /api/v1/connections/{connection_id}/trigger-ingress` This endpoint is in beta. Its request and response may change. Creates the webhook URL the app sends this connection's events to. Register the URL in your app's settings at the provider, then [set the signing secret](https://docs.scalekit.com/agentkit/reference/triggers/set-a-signing-secret/). Only a connection whose app has a webhook trigger can have one. If the connection already has a URL, it's returned unchanged, so retrying is safe. Keep the URL private. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | string | Yes | ID of the connection, in this environment. Up to 64 characters. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `connection_id` | string | ID of the connection this webhook URL belongs to. | | `id` | string | Unique identifier of the connection's webhook endpoint. | | `masked_signing_secret` | string | The stored secret with all but its last four characters replaced, or every character replaced for a secret of 8 characters or fewer. Empty when none is configured. Enough to compare against the secret the provider's app configuration shows, and never enough to sign with. | | `previous_signing_secret_expires_at` | string | When the previous secret stops being accepted even if the rotation is never confirmed: 24 hours after the rotation started. Absent when no rotation is in progress. | | `rotation_in_progress` | boolean | Whether the previous signing secret is still accepted alongside the current one. True from the moment a different secret replaces a stored one until the rotation is confirmed, or until previous_signing_secret_expires_at passes. | | `signing_secret_configured` | boolean | Whether a signing secret is stored. While false, every event sent to the URL is answered with 503 so the provider retries it, and the provider's own URL verification fails. | | `signing_secret_rotation_id` | string | Identifies the latest signing-secret rotation; it increases by one each time a rotation starts. Zero when none has ever started. Pass this value to [Confirm a signing secret rotation](https://docs.scalekit.com/agentkit/reference/triggers/confirm-a-signing-secret-rotation/) to confirm this specific rotation. A value naming a rotation that a newer one has since replaced is refused rather than confirming the wrong rotation. | | `url` | string | The URL to paste into the provider's app configuration as the event destination. It is built on your environment's domain, using its custom domain when one is set, and ends in a random token unique to this connection, so treat it as confidential. Returned whether or not a signing secret has been set. The URL accepts request bodies up to 1 MB and answers a larger one with 413. Until a signing secret is set it answers events with 503, so the provider retries them; an event that fails signature verification is answered with 400. | **Errors** - `400`: Invalid request, nothing created - connection_id is not a valid connection ID; the connection's provider publishes no webhook trigger, so it has no use for a webhook URL (error_code TRIGGER_INGRESS_NOT_APPLICABLE); or the provider's webhook trigger definitions are temporarily unavailable (error_code TRIGGER_INGRESS_DEFINITIONS_UNAVAILABLE) - `404`: Not found - the connection does not exist in this environment, or Triggers isn't turned on for your environment - `500`: Internal error - the webhook URL could not be created or read back; retry the request **Request** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connections/conn_133400101014997214/trigger-ingress" \ -H "Authorization: Bearer $TOKEN" ``` **Response** ```json { "id": "tie_149528599802678310", "connection_id": "conn_133400101014997214", "url": "https://your-env.scalekit.dev/triggers/v1/q7Rk2mVx9TbL4nWc8YpZ3s", "signing_secret_configured": false, "masked_signing_secret": "", "rotation_in_progress": false, "signing_secret_rotation_id": "0" } ``` **Python SDK:** `scalekit_client.actions.triggers.create_ingress_endpoint` Create a connection's webhook URL, or return the existing one. ```python scalekit_client.actions.triggers.create_ingress_endpoint( connection_id: str, ) -> TriggerIngressEndpoint ``` Example: ```python result = scalekit_client.actions.triggers.create_ingress_endpoint( connection_id="conn_133400101014997214", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | `str` | Yes | The connection (`conn_...`). | Returns `TriggerIngressEndpoint`: The URL to paste into the provider's app configuration. ## Get a webhook URL (beta) `GET /api/v1/connections/{connection_id}/trigger-ingress` This endpoint is in beta. Its request and response may change. Gets the connection's webhook URL, whether a signing secret is set, the secret's last four characters, and whether a secret rotation is in progress. The secret itself is never returned. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | string | Yes | ID of the connection, in this environment. Up to 64 characters. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `connection_id` | string | ID of the connection this webhook URL belongs to. | | `id` | string | Unique identifier of the connection's webhook endpoint. | | `masked_signing_secret` | string | The stored secret with all but its last four characters replaced, or every character replaced for a secret of 8 characters or fewer. Empty when none is configured. Enough to compare against the secret the provider's app configuration shows, and never enough to sign with. | | `previous_signing_secret_expires_at` | string | When the previous secret stops being accepted even if the rotation is never confirmed: 24 hours after the rotation started. Absent when no rotation is in progress. | | `rotation_in_progress` | boolean | Whether the previous signing secret is still accepted alongside the current one. True from the moment a different secret replaces a stored one until the rotation is confirmed, or until previous_signing_secret_expires_at passes. | | `signing_secret_configured` | boolean | Whether a signing secret is stored. While false, every event sent to the URL is answered with 503 so the provider retries it, and the provider's own URL verification fails. | | `signing_secret_rotation_id` | string | Identifies the latest signing-secret rotation; it increases by one each time a rotation starts. Zero when none has ever started. Pass this value to [Confirm a signing secret rotation](https://docs.scalekit.com/agentkit/reference/triggers/confirm-a-signing-secret-rotation/) to confirm this specific rotation. A value naming a rotation that a newer one has since replaced is refused rather than confirming the wrong rotation. | | `url` | string | The URL to paste into the provider's app configuration as the event destination. It is built on your environment's domain, using its custom domain when one is set, and ends in a random token unique to this connection, so treat it as confidential. Returned whether or not a signing secret has been set. The URL accepts request bodies up to 1 MB and answers a larger one with 413. Until a signing secret is set it answers events with 503, so the provider retries them; an event that fails signature verification is answered with 400. | **Errors** - `400`: Invalid request - connection_id is not a valid connection ID - `404`: Not found - no webhook URL has been created for this connection yet (error_code TRIGGER_INGRESS_ENDPOINT_NOT_CREATED; call [Create a webhook URL](https://docs.scalekit.com/agentkit/reference/triggers/create-a-webhook-url/)), the connection does not exist in this environment (error_code RESOURCE_NOT_FOUND), or Triggers isn't turned on for your environment (error_code RESOURCE_NOT_FOUND) - `500`: Internal error - the webhook URL or its signing-secret state could not be read; retry the request **Request** ```bash curl -sS -X GET \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connections/conn_133400101014997214/trigger-ingress" \ -H "Authorization: Bearer $TOKEN" ``` **Response** ```json { "id": "tie_149528599802678310", "connection_id": "conn_133400101014997214", "url": "https://your-env.scalekit.dev/triggers/v1/q7Rk2mVx9TbL4nWc8YpZ3s", "signing_secret_configured": true, "masked_signing_secret": "****************************1bef", "rotation_in_progress": false, "signing_secret_rotation_id": "0" } ``` **Python SDK:** `scalekit_client.actions.triggers.get_ingress_endpoint` Read the webhook URL a provider sends a connection's events to. ```python scalekit_client.actions.triggers.get_ingress_endpoint( connection_id: str, ) -> TriggerIngressEndpoint ``` Example: ```python result = scalekit_client.actions.triggers.get_ingress_endpoint( connection_id="conn_133400101014997214", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | `str` | Yes | The connection (`conn_...`). | Returns `TriggerIngressEndpoint`: The URL and the state of its signing secret (never the secret itself). ## Set a signing secret (beta) `PUT /api/v1/connections/{connection_id}/trigger-ingress/signing-secret` This endpoint is in beta. Its request and response may change. Saves the secret the app signs its webhook events with, copied from your app's settings at the provider, so Scalekit can verify each event. Until a secret is set, the webhook URL doesn't accept events. If a secret is already set, a new one starts a rotation: both are accepted until you [confirm the rotation](https://docs.scalekit.com/agentkit/reference/triggers/confirm-a-signing-secret-rotation/), or for at most 24 hours. Read the secret from your server's secrets, never from code. For an app that sends no signature of its own, [generate a secret](https://docs.scalekit.com/agentkit/reference/triggers/generate-a-signing-secret/) instead. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | string | Yes | ID of the connection, in this environment. Up to 64 characters. | **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `signing_secret` | string | Yes | The secret the provider signs its events with, copied from the provider's app configuration. For Slack this is the Signing Secret under Basic Information, not an API token. Between 8 and 4096 characters. For Gong, the RSA public key shown by 'Show public key'. Never returned by any endpoint and never written to a log. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `connection_id` | string | ID of the connection this webhook URL belongs to. | | `id` | string | Unique identifier of the connection's webhook endpoint. | | `masked_signing_secret` | string | The stored secret with all but its last four characters replaced, or every character replaced for a secret of 8 characters or fewer. Empty when none is configured. Enough to compare against the secret the provider's app configuration shows, and never enough to sign with. | | `previous_signing_secret_expires_at` | string | When the previous secret stops being accepted even if the rotation is never confirmed: 24 hours after the rotation started. Absent when no rotation is in progress. | | `rotation_in_progress` | boolean | Whether the previous signing secret is still accepted alongside the current one. True from the moment a different secret replaces a stored one until the rotation is confirmed, or until previous_signing_secret_expires_at passes. | | `signing_secret_configured` | boolean | Whether a signing secret is stored. While false, every event sent to the URL is answered with 503 so the provider retries it, and the provider's own URL verification fails. | | `signing_secret_rotation_id` | string | Identifies the latest signing-secret rotation; it increases by one each time a rotation starts. Zero when none has ever started. Pass this value to [Confirm a signing secret rotation](https://docs.scalekit.com/agentkit/reference/triggers/confirm-a-signing-secret-rotation/) to confirm this specific rotation. A value naming a rotation that a newer one has since replaced is refused rather than confirming the wrong rotation. | | `url` | string | The URL to paste into the provider's app configuration as the event destination. It is built on your environment's domain, using its custom domain when one is set, and ends in a random token unique to this connection, so treat it as confidential. Returned whether or not a signing secret has been set. The URL accepts request bodies up to 1 MB and answers a larger one with 413. Until a signing secret is set it answers events with 503, so the provider retries them; an event that fails signature verification is answered with 400. | **Errors** - `400`: Invalid request, nothing written - connection_id is not a valid connection ID; signing_secret is shorter than 8 characters once leading and trailing whitespace is removed, or longer than 4096 characters; the connection's provider sends no signature of its own, so its secret is generated by Scalekit rather than set (error_code TRIGGER_SIGNING_SECRET_GENERATED_ONLY; call [Generate a signing secret](https://docs.scalekit.com/agentkit/reference/triggers/generate-a-signing-secret/) instead); or the provider's webhook trigger definitions are temporarily unavailable (error_code TRIGGER_INGRESS_DEFINITIONS_UNAVAILABLE) - `404`: Not found - no webhook URL has been created for this connection yet (error_code TRIGGER_INGRESS_ENDPOINT_NOT_CREATED; call [Create a webhook URL](https://docs.scalekit.com/agentkit/reference/triggers/create-a-webhook-url/) first), the connection does not exist in this environment (error_code RESOURCE_NOT_FOUND), or Triggers isn't turned on for your environment (error_code RESOURCE_NOT_FOUND) - `409`: Conflict - a rotation is already in progress. Confirm it with [Confirm a signing secret rotation](https://docs.scalekit.com/agentkit/reference/triggers/confirm-a-signing-secret-rotation/), or wait for the previous secret's expiry, before setting another secret - `500`: Internal error - the signing secret could not be stored; retry the request **Request** ```bash curl -sS -X PUT \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connections/conn_133400101014997214/trigger-ingress/signing-secret" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "signing_secret": "" }' ``` **Response** ```json { "id": "tie_149528599802678310", "connection_id": "conn_133400101014997214", "url": "https://your-env.scalekit.dev/triggers/v1/q7Rk2mVx9TbL4nWc8YpZ3s", "signing_secret_configured": true, "masked_signing_secret": "****************************a4f9", "rotation_in_progress": true, "signing_secret_rotation_id": "1", "previous_signing_secret_expires_at": "2026-10-29T09:30:00Z" } ``` **Python SDK:** `scalekit_client.actions.triggers.set_signing_secret` Store the signing secret the provider signs its events with. The first call stores it. A call with a different secret starts a rotation: both secrets are accepted until `confirm_signing_secret_rotation` or for 24 hours. Not retried on `UNAVAILABLE`, because a retry could advance the rotation. ```python scalekit_client.actions.triggers.set_signing_secret( connection_id: str, signing_secret: str, ) -> TriggerIngressEndpoint ``` Example: ```python import os result = scalekit_client.actions.triggers.set_signing_secret( connection_id="conn_133400101014997214", signing_secret=os.environ["SLACK_SIGNING_SECRET"], ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | `str` | Yes | The connection (`conn_...`). | | `signing_secret` | `str` | Yes | The secret from the provider's app configuration (8-4096 characters; surrounding whitespace is trimmed by the server). | Returns `TriggerIngressEndpoint`: The endpoint, with `masked_signing_secret` and rotation state. ## Confirm a signing secret rotation (beta) `POST /api/v1/connections/{connection_id}/trigger-ingress/signing-secret/confirm` This endpoint is in beta. Its request and response may change. Retires the previous signing secret, so only the new one is accepted. Call it once the app signs with the new secret. Pass `signing_secret_rotation_id` from the latest set or get response, so you confirm the rotation you checked. With no rotation in progress, the call succeeds and changes nothing. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | string | Yes | ID of the connection, in this environment. Up to 64 characters. | **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `signing_secret_rotation_id` | string | No | The rotation to confirm: the signing_secret_rotation_id from the latest [Set a signing secret](https://docs.scalekit.com/agentkit/reference/triggers/set-a-signing-secret/) or [Get a webhook URL](https://docs.scalekit.com/agentkit/reference/triggers/get-a-webhook-url/) response. Required while a rotation is in progress; a missing or non-matching value is then refused with 400, rather than confirming a rotation you did not look at. Ignored when no rotation is in progress. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `connection_id` | string | ID of the connection this webhook URL belongs to. | | `id` | string | Unique identifier of the connection's webhook endpoint. | | `masked_signing_secret` | string | The stored secret with all but its last four characters replaced, or every character replaced for a secret of 8 characters or fewer. Empty when none is configured. Enough to compare against the secret the provider's app configuration shows, and never enough to sign with. | | `previous_signing_secret_expires_at` | string | When the previous secret stops being accepted even if the rotation is never confirmed: 24 hours after the rotation started. Absent when no rotation is in progress. | | `rotation_in_progress` | boolean | Whether the previous signing secret is still accepted alongside the current one. True from the moment a different secret replaces a stored one until the rotation is confirmed, or until previous_signing_secret_expires_at passes. | | `signing_secret_configured` | boolean | Whether a signing secret is stored. While false, every event sent to the URL is answered with 503 so the provider retries it, and the provider's own URL verification fails. | | `signing_secret_rotation_id` | string | Identifies the latest signing-secret rotation; it increases by one each time a rotation starts. Zero when none has ever started. Pass this value to [Confirm a signing secret rotation](https://docs.scalekit.com/agentkit/reference/triggers/confirm-a-signing-secret-rotation/) to confirm this specific rotation. A value naming a rotation that a newer one has since replaced is refused rather than confirming the wrong rotation. | | `url` | string | The URL to paste into the provider's app configuration as the event destination. It is built on your environment's domain, using its custom domain when one is set, and ends in a random token unique to this connection, so treat it as confidential. Returned whether or not a signing secret has been set. The URL accepts request bodies up to 1 MB and answers a larger one with 413. Until a signing secret is set it answers events with 503, so the provider retries them; an event that fails signature verification is answered with 400. | **Errors** - `400`: Invalid request - connection_id is not a valid connection ID, or a rotation is in progress and signing_secret_rotation_id is missing or does not match it, for example because a newer rotation has started. The message names the current rotation ID; read the endpoint again with [Get a webhook URL](https://docs.scalekit.com/agentkit/reference/triggers/get-a-webhook-url/) before confirming - `404`: Not found - no webhook URL has been created for this connection yet (error_code TRIGGER_INGRESS_ENDPOINT_NOT_CREATED; call [Create a webhook URL](https://docs.scalekit.com/agentkit/reference/triggers/create-a-webhook-url/) first), the connection does not exist in this environment (error_code RESOURCE_NOT_FOUND), or Triggers isn't turned on for your environment (error_code RESOURCE_NOT_FOUND) - `500`: Internal error - the rotation could not be confirmed; retry the request **Request** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connections/conn_133400101014997214/trigger-ingress/signing-secret/confirm" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "signing_secret_rotation_id": "1" }' ``` **Response** ```json { "id": "tie_149528599802678310", "connection_id": "conn_133400101014997214", "url": "https://your-env.scalekit.dev/triggers/v1/q7Rk2mVx9TbL4nWc8YpZ3s", "signing_secret_configured": true, "masked_signing_secret": "****************************a4f9", "rotation_in_progress": false, "signing_secret_rotation_id": "1" } ``` **Python SDK:** `scalekit_client.actions.triggers.confirm_signing_secret_rotation` Finish a signing-secret rotation so only the new secret is accepted. ```python scalekit_client.actions.triggers.confirm_signing_secret_rotation( connection_id: str, signing_secret_rotation_id: int | None = None, ) -> TriggerIngressEndpoint ``` Example: ```python result = scalekit_client.actions.triggers.confirm_signing_secret_rotation( connection_id="conn_133400101014997214", signing_secret_rotation_id=1, ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | `str` | Yes | The connection (`conn_...`). | | `signing_secret_rotation_id` | `int \| None` | No | `TriggerIngressEndpoint.signing_secret_rotation_id` of the rotation to confirm. Required while a rotation is in progress; ignored when none is. | Returns `TriggerIngressEndpoint`: The endpoint after the confirmation. ## Generate a signing secret (beta) `POST /api/v1/connections/{connection_id}/trigger-ingress/signing-secret/generate` This endpoint is in beta. Its request and response may change. Generates a signing secret for an app that doesn't sign its events (`provides_bearer_secret` is `true` on its triggers), and returns it once. A generated secret starts with `skwh_`. Give it to the app, which sends it with every event: in an `Authorization: Bearer` header, in the `X-Scalekit-Webhook-Secret` header, or as the last segment of `url_with_secret` when the app can't set a header. Store it with your server's secrets now; it's never returned again. If a secret already exists, a new one starts a rotation. An app that signs its own events takes its secret through [Set a signing secret](https://docs.scalekit.com/agentkit/reference/triggers/set-a-signing-secret/) instead. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | string | Yes | The connection whose webhook URL receives the generated secret. Between 1 and 64 characters. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `endpoint` | object | The connection's webhook endpoint after the secret was stored, rendered exactly as [Get a webhook URL](https://docs.scalekit.com/agentkit/reference/triggers/get-a-webhook-url/) renders it: the secret appears here only in masked form, and rotation_in_progress is true when this call started a rotation. | | `endpoint.connection_id` | string | ID of the connection this webhook URL belongs to. | | `endpoint.id` | string | Unique identifier of the connection's webhook endpoint. | | `endpoint.masked_signing_secret` | string | The stored secret with all but its last four characters replaced, or every character replaced for a secret of 8 characters or fewer. Empty when none is configured. Enough to compare against the secret the provider's app configuration shows, and never enough to sign with. | | `endpoint.previous_signing_secret_expires_at` | string | When the previous secret stops being accepted even if the rotation is never confirmed: 24 hours after the rotation started. Absent when no rotation is in progress. | | `endpoint.rotation_in_progress` | boolean | Whether the previous signing secret is still accepted alongside the current one. True from the moment a different secret replaces a stored one until the rotation is confirmed, or until previous_signing_secret_expires_at passes. | | `endpoint.signing_secret_configured` | boolean | Whether a signing secret is stored. While false, every event sent to the URL is answered with 503 so the provider retries it, and the provider's own URL verification fails. | | `endpoint.signing_secret_rotation_id` | string | Identifies the latest signing-secret rotation; it increases by one each time a rotation starts. Zero when none has ever started. Pass this value to [Confirm a signing secret rotation](https://docs.scalekit.com/agentkit/reference/triggers/confirm-a-signing-secret-rotation/) to confirm this specific rotation. A value naming a rotation that a newer one has since replaced is refused rather than confirming the wrong rotation. | | `endpoint.url` | string | The URL to paste into the provider's app configuration as the event destination. It is built on your environment's domain, using its custom domain when one is set, and ends in a random token unique to this connection, so treat it as confidential. Returned whether or not a signing secret has been set. The URL accepts request bodies up to 1 MB and answers a larger one with 413. Until a signing secret is set it answers events with 503, so the provider retries them; an event that fails signature verification is answered with 400. | | `signing_secret` | string | The generated secret, in plain text. Returned only in this response and never again by any endpoint, nor written to a log. The provider presents it back on every event in exactly one of three ways: an `Authorization: Bearer ` header, the `X-Scalekit-Webhook-Secret` header, or the final path segment of url_with_secret. | | `url_with_secret` | string | Endpoint.url with the secret appended as a final path segment. Paste this into the provider's console when the provider cannot send a custom header. It is as sensitive as signing_secret and is likewise returned only in this response. | **Errors** - `400`: Invalid request, nothing written - connection_id is not a valid connection ID; the connection's provider signs its own events, so its secret is copied from the provider's app configuration and stored with [Set a signing secret](https://docs.scalekit.com/agentkit/reference/triggers/set-a-signing-secret/) instead (error_code TRIGGER_SIGNING_SECRET_NOT_GENERATABLE); or the provider's webhook trigger definitions are temporarily unavailable (error_code TRIGGER_INGRESS_DEFINITIONS_UNAVAILABLE) - `404`: Not found - no webhook URL has been created for this connection yet (error_code TRIGGER_INGRESS_ENDPOINT_NOT_CREATED; call [Create a webhook URL](https://docs.scalekit.com/agentkit/reference/triggers/create-a-webhook-url/) first), the connection does not exist in this environment (error_code RESOURCE_NOT_FOUND), or Triggers isn't turned on for your environment (error_code RESOURCE_NOT_FOUND) - `409`: Conflict - a rotation is already in progress. Confirm it with [Confirm a signing secret rotation](https://docs.scalekit.com/agentkit/reference/triggers/confirm-a-signing-secret-rotation/), or wait for the previous secret's expiry, before generating another secret - `500`: Internal error - the secret could not be generated or stored, or the endpoint could not be read back; retry the request **Request** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/connections/conn_133400101014998936/trigger-ingress/signing-secret/generate" \ -H "Authorization: Bearer $TOKEN" ``` **Response** ```json { "signing_secret": "skwh_Xq3vR8nT1bLm6KpZ2wYc9DfH4sJg7AeU0oNi5tGr1Bk", "url_with_secret": "https://your-env.scalekit.dev/triggers/v1/h3Lp8wQz1nRt6vYc4KmXb9/skwh_Xq3vR8nT1bLm6KpZ2wYc9DfH4sJg7AeU0oNi5tGr1Bk", "endpoint": { "id": "tie_149528599802681734", "connection_id": "conn_133400101014998936", "url": "https://your-env.scalekit.dev/triggers/v1/h3Lp8wQz1nRt6vYc4KmXb9", "signing_secret_configured": true, "masked_signing_secret": "********************************************r1Bk", "rotation_in_progress": false, "signing_secret_rotation_id": "0" } } ``` **Python SDK:** `scalekit_client.actions.triggers.generate_signing_secret` Generate a signing secret, for providers that use one Scalekit creates. The secret is returned in plaintext only in this response; it is left out of `repr()`. Not retried on `UNAVAILABLE`: a retry would replace the secret. ```python scalekit_client.actions.triggers.generate_signing_secret( connection_id: str, ) -> GenerateTriggerSigningSecretResponse ``` Example: ```python result = scalekit_client.actions.triggers.generate_signing_secret( connection_id="conn_133400101014998936", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `connection_id` | `str` | Yes | The connection (`conn_...`). | Returns `GenerateTriggerSigningSecretResponse`: The endpoint, the secret and the URL that includes it. ## List deliveries (beta) `GET /api/v1/trigger-delivery/endpoints/{endpoint_id}/deliveries` This endpoint is in beta. Its request and response may change. Lists the trigger events sent to one of your delivery endpoints, newest first, one entry per event however many attempts it took. Filter by `status`, by `channel` (a connection or subscription ID) and by time with `after` and `before`. Deliveries are kept for 90 days. Pass `next_page_token` as `page_token` to read older ones. The event's payload isn't returned. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `endpoint_id` | string | Yes | ID of your delivery endpoint (`ep_...`), the URL on your server that you added under **Developers** > **Trigger Delivery**. [Look up a delivery](https://docs.scalekit.com/agentkit/reference/triggers/look-up-a-delivery/) returns it as `endpoint_id` on each attempt. | **Query parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `after` | string | No | Only deliveries of events created after this instant. Omit it to read back to the oldest retained delivery. Deliveries are kept for 90 days, so a value earlier than that is moved forward to the start of the retained window. Refused when later than `before`. Read to the second. | | `before` | string | No | Only deliveries of events created before this instant. Omit it to start from the newest delivery. A value later than the current time is treated as the current time. With `after`, reads a time range; the list stays newest first within it. Read to the second. | | `channel` | string | No | Narrows the list to deliveries of events from one connection (a `conn_` ID) or one trigger subscription (a `trgs_` ID). The ID must exist in this environment; any other value is refused with 400. At most 64 characters. | | `page_size` | integer | No | Maximum number of deliveries to return, newest first. Defaults to 20 when omitted or zero; a larger value than 100 is capped at 100 rather than refused. | | `page_token` | string | No | The next_page_token from a previous response, to read the next page of older deliveries. Omit it to read the newest page. A token is bound to the environment, the endpoint and the filters it was issued with: a token sent with a different endpoint or different filters is refused rather than mixing two result sets. At most 1024 characters. | | `status` | string (enum) | No | Narrows the list to deliveries in this state. Omit it, or send TRIGGER_DELIVERY_STATUS_UNSPECIFIED, for every state. - TRIGGER_DELIVERY_STATUS_DELIVERED: An attempt succeeded. No further attempt is made. - TRIGGER_DELIVERY_STATUS_PENDING: Accepted for delivery; no attempt has finished yet. - TRIGGER_DELIVERY_STATUS_FAILED: Every scheduled attempt failed. No further attempt is made unless one is started by hand. - TRIGGER_DELIVERY_STATUS_RETRYING: An attempt failed and another is scheduled, at next_attempt_at. One of: `TRIGGER_DELIVERY_STATUS_DELIVERED`, `TRIGGER_DELIVERY_STATUS_PENDING`, `TRIGGER_DELIVERY_STATUS_FAILED`, `TRIGGER_DELIVERY_STATUS_RETRYING`. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `deliveries` | array of object | One page of the endpoint's deliveries that match the filters, newest first. | | `deliveries.connection_id` | string | ID of the connection the event came from. Present even after the connection is deleted. Empty when the event carries none. | | `deliveries.connection_key_id` | string | Name of the connection the event came from, the value other AgentKit endpoints take as `connector`. Empty when the connection no longer exists. | | `deliveries.connection_provider` | string | Provider of the connection the event came from. Empty when the connection no longer exists. | | `deliveries.endpoint_id` | string | ID of the endpoint the event was delivered to. | | `deliveries.event_id` | string | Event ID of the event: the payload's `dedupe_key` and `correlation_id`. Empty when the event was sent without one. | | `deliveries.message_timestamp` | string | When the event was accepted for delivery. | | `deliveries.msg_id` | string | Message ID of the event, sent to the endpoint as the `webhook-id` header. | | `deliveries.next_attempt_at` | string | When the next attempt is scheduled. Present only while status is TRIGGER_DELIVERY_STATUS_RETRYING. | | `deliveries.status` | string (enum) | The delivery's state across all its attempts. One of: `TRIGGER_DELIVERY_STATUS_DELIVERED`, `TRIGGER_DELIVERY_STATUS_PENDING`, `TRIGGER_DELIVERY_STATUS_FAILED`, `TRIGGER_DELIVERY_STATUS_RETRYING`. | | `deliveries.subscription_id` | string | ID of the trigger subscription that sent the event. Present even after the subscription is deleted. Empty when the event carries none. | | `deliveries.trigger_mode` | string (enum) | How the trigger detects events, from the newest version of its definition: WEBHOOK or POLL. The trigger's mode now, not a record of how this one event was detected. TRIGGER_MODE_UNSPECIFIED when the subscription or the definition no longer exists. One of: `WEBHOOK`, `POLL`. | | `deliveries.trigger_name` | string | Key of the trigger that fired, as the subscription was created with. Empty when the subscription no longer exists. | | `next_page_token` | string | Pass this as page_token, with the same endpoint_id and filters, to read the next page of older deliveries. Empty when there are no older deliveries. | **Errors** - `400`: Invalid request - endpoint_id is missing, is longer than 256 characters, contains characters other than letters, digits, `.`, `_` and `-`, or does not name a trigger delivery endpoint in this environment; status is not a known delivery status; channel is not a connection ID (`conn_`) or trigger subscription ID (`trgs_`) in this environment; `after` or `before` is not a valid timestamp, or `after` is later than `before`; or page_token is malformed or was issued for a different endpoint or set of filters - `404`: Not found - Triggers isn't turned on for your environment - `429`: Too many requests - deliveries are being read too often. Retry shortly; the request is not retried on your behalf - `500`: Internal error - the deliveries could not be read; retry the request **Request** ```bash curl -sS -G -X GET \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/trigger-delivery/endpoints/ep_2qGv8XHk3mT5bJr9LwNc7pZeYfA/deliveries" \ -H "Authorization: Bearer $TOKEN" \ --data-urlencode "status=TRIGGER_DELIVERY_STATUS_FAILED" ``` **Response** ```json { "deliveries": [ { "msg_id": "msg_2qGvA4uPf2HjQ7zTn5KrW8dCxLb", "event_id": "ca_5F133400349586228019.trgs_5F141020933185734981.00Q5g00000AbCdEFGJ", "endpoint_id": "ep_2qGv8XHk3mT5bJr9LwNc7pZeYfA", "status": "TRIGGER_DELIVERY_STATUS_FAILED", "message_timestamp": "2026-10-28T10:02:44.910Z", "subscription_id": "trgs_141020933185734981", "trigger_name": "salesforce_lead_created", "trigger_mode": "POLL", "connection_id": "conn_133400101014995480", "connection_key_id": "salesforce", "connection_provider": "SALESFORCE" } ], "next_page_token": "" } ``` **Python SDK:** `scalekit_client.actions.triggers.list_deliveries` List trigger events sent to one of your endpoints, newest first. ```python scalekit_client.actions.triggers.list_deliveries( endpoint_id: str, page_size: int | None = None, page_token: str | None = None, status: TriggerDeliveryStatus | str | int | None = None, channel: str | None = None, after: datetime | None = None, before: datetime | None = None, ) -> ListTriggerDeliveriesByEndpointResponse ``` Example: ```python from scalekit.actions import TriggerDeliveryStatus result = scalekit_client.actions.triggers.list_deliveries( endpoint_id="ep_2qGv8XHk3mT5bJr9LwNc7pZeYfA", status=TriggerDeliveryStatus.FAILED, ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `endpoint_id` | `str` | Yes | ID of your delivery endpoint (`ep_...`), the URL on your server that you added under **Developers** > **Trigger Delivery**. [Look up a delivery](https://docs.scalekit.com/agentkit/reference/triggers/look-up-a-delivery/) returns it as `endpoint_id` on each attempt. | | `page_size` | `int \| None` | No | Messages per page; the server default is 20, capped at 100. | | `page_token` | `str \| None` | No | `next_page_token` of a previous page (with the same filters). | | `status` | `TriggerDeliveryStatus \| str \| int \| None` | No | Only messages in this delivery state. | | `channel` | `str \| None` | No | Only messages for this connection (`conn_...`) or subscription (`trgs_...`). | | `after` | `datetime \| None` | No | Only messages after this aware instant (at most 90 days back). | | `before` | `datetime \| None` | No | Only messages before this aware instant. | Returns `ListTriggerDeliveriesByEndpointResponse`: One page of messages and `next_page_token`. ## List the attempts for a delivery (beta) `GET /api/v1/trigger-delivery/endpoints/{endpoint_id}/deliveries/{msg_id}/attempts` This endpoint is in beta. Its request and response may change. Lists every attempt to deliver one event to one of your endpoints, oldest first: the first attempt, each automatic retry and each resend. Each attempt has its HTTP status code, your endpoint's response body (its first 4096 bytes) and how long it took. Returns at most the newest 50 attempts; `truncated` is `true` when there are more. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `endpoint_id` | string | Yes | ID of your delivery endpoint (`ep_...`), the URL on your server that you added under **Developers** > **Trigger Delivery**. [Look up a delivery](https://docs.scalekit.com/agentkit/reference/triggers/look-up-a-delivery/) returns it as `endpoint_id` on each attempt. | | `msg_id` | string | Yes | Message ID of the delivery, as returned in msg_id by [List deliveries](https://docs.scalekit.com/agentkit/reference/triggers/list-deliveries/): `msg_` followed by 1 to 64 letters or digits. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `attempts` | array of object | The delivery's attempts to this endpoint, oldest first. | | `attempts.endpoint_id` | string | ID of the endpoint this attempt was made against. | | `attempts.endpoint_url` | string | URL this attempt was sent to. | | `attempts.id` | string | ID of this delivery attempt. | | `attempts.msg_id` | string | ID of the trigger event message this attempt delivered. Every attempt to deliver the same event shares this ID. | | `attempts.response` | string | Response body the endpoint returned for this attempt. A long body may be truncated. | | `attempts.response_duration_ms` | integer | How long the endpoint took to respond to this attempt, in milliseconds. | | `attempts.response_status_code` | integer | HTTP status code the endpoint returned for this attempt. | | `attempts.response_truncated` | boolean | True when `response` was cut to its first 4096 bytes. Set only by [List the attempts for a delivery](https://docs.scalekit.com/agentkit/reference/triggers/list-attempts-for-a-delivery/) and [Look up a delivery](https://docs.scalekit.com/agentkit/reference/triggers/look-up-a-delivery/), which apply that cap. | | `attempts.status` | string (enum) | Whether this attempt succeeded, is still in flight, failed, or was canceled. A failed delivery is retried, so FAIL on one attempt does not mean the event was never delivered — check the newest attempt for that message. One of: `SUCCESS`, `PENDING`, `FAIL`, `SENDING`, `CANCELED`. | | `attempts.timestamp` | string | When this attempt was made. | | `attempts.trigger_type` | string (enum) | Why this attempt was made: TRIGGER_DELIVERY_ATTEMPT_TRIGGER_TYPE_SCHEDULED for the first attempt or an automatic retry, TRIGGER_DELIVERY_ATTEMPT_TRIGGER_TYPE_MANUAL for a retry someone started by hand. One of: `TRIGGER_DELIVERY_ATTEMPT_TRIGGER_TYPE_SCHEDULED`, `TRIGGER_DELIVERY_ATTEMPT_TRIGGER_TYPE_MANUAL`. | | `truncated` | boolean | True when the delivery has more than the 50 attempts returned. The newest 50 are returned, so the first attempt is missing when this is true. | **Errors** - `400`: Invalid request - endpoint_id is missing, is longer than 256 characters or contains characters other than letters, digits, `.`, `_` and `-`; msg_id is not a message ID (`msg_` followed by 1 to 64 letters or digits); or the endpoint or the message does not exist in this environment - `404`: Not found - Triggers isn't turned on for your environment - `429`: Too many requests - deliveries are being read too often. Retry shortly; the request is not retried on your behalf - `500`: Internal error - the attempts could not be read; retry the request **Request** ```bash curl -sS -X GET \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/trigger-delivery/endpoints/ep_2qGv8XHk3mT5bJr9LwNc7pZeYfA/deliveries/msg_2qGvA1sKd8RtY6nWm3LpX9cBhVe/attempts" \ -H "Authorization: Bearer $TOKEN" ``` **Response** ```json { "attempts": [ { "id": "atmpt_2qGvA2bNf7KcW4xRm8TjP5sLhYd", "msg_id": "msg_2qGvA1sKd8RtY6nWm3LpX9cBhVe", "endpoint_id": "ep_2qGv8XHk3mT5bJr9LwNc7pZeYfA", "endpoint_url": "https://api.example.com/scalekit/triggers", "status": "SUCCESS", "response_status_code": 200, "response": "{\"ok\":true}", "response_truncated": false, "response_duration_ms": 84, "timestamp": "2026-10-28T09:17:45.020Z", "trigger_type": "TRIGGER_DELIVERY_ATTEMPT_TRIGGER_TYPE_SCHEDULED" } ], "truncated": false } ``` **Python SDK:** `scalekit_client.actions.triggers.list_delivery_attempts_by_message` List every attempt to deliver one message to one of your endpoints. ```python scalekit_client.actions.triggers.list_delivery_attempts_by_message( endpoint_id: str, msg_id: str, ) -> ListTriggerDeliveryAttemptsByMessageResponse ``` Example: ```python result = scalekit_client.actions.triggers.list_delivery_attempts_by_message( endpoint_id="ep_2qGv8XHk3mT5bJr9LwNc7pZeYfA", msg_id="msg_2qGvA1sKd8RtY6nWm3LpX9cBhVe", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `endpoint_id` | `str` | Yes | ID of your delivery endpoint (`ep_...`), the URL on your server that you added under **Developers** > **Trigger Delivery**. [Look up a delivery](https://docs.scalekit.com/agentkit/reference/triggers/look-up-a-delivery/) returns it as `endpoint_id` on each attempt. | | `msg_id` | `str` | Yes | The message (`msg_...`). | Returns `ListTriggerDeliveryAttemptsByMessageResponse`: Up to 50 attempts, oldest first, and `truncated`. ## List recent delivery attempts (beta) `GET /api/v1/trigger-delivery/endpoints/{endpoint_id}/attempts` This endpoint is in beta. Its request and response may change. Lists the most recent attempts to deliver trigger events to one of your endpoints, newest first, up to `page_size` (default 20, at most 100). There's no page token for older attempts. A failed attempt is retried, so check the newest attempt for an event before you decide it wasn't delivered. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `endpoint_id` | string | Yes | ID of your delivery endpoint (`ep_...`), the URL on your server that you added under **Developers** > **Trigger Delivery**. [Look up a delivery](https://docs.scalekit.com/agentkit/reference/triggers/look-up-a-delivery/) returns it as `endpoint_id` on each attempt. | **Query parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `page_size` | integer | No | Maximum number of attempts to return, newest first. Defaults to 20 when omitted or zero; a larger value than 100 is capped at 100 rather than refused. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `attempts` | array of object | The endpoint's most recent delivery attempts, newest first. | | `attempts.endpoint_id` | string | ID of the endpoint this attempt was made against. | | `attempts.endpoint_url` | string | URL this attempt was sent to. | | `attempts.id` | string | ID of this delivery attempt. | | `attempts.msg_id` | string | ID of the trigger event message this attempt delivered. Every attempt to deliver the same event shares this ID. | | `attempts.response` | string | Response body the endpoint returned for this attempt. A long body may be truncated. | | `attempts.response_duration_ms` | integer | How long the endpoint took to respond to this attempt, in milliseconds. | | `attempts.response_status_code` | integer | HTTP status code the endpoint returned for this attempt. | | `attempts.response_truncated` | boolean | True when `response` was cut to its first 4096 bytes. Set only by [List the attempts for a delivery](https://docs.scalekit.com/agentkit/reference/triggers/list-attempts-for-a-delivery/) and [Look up a delivery](https://docs.scalekit.com/agentkit/reference/triggers/look-up-a-delivery/), which apply that cap. | | `attempts.status` | string (enum) | Whether this attempt succeeded, is still in flight, failed, or was canceled. A failed delivery is retried, so FAIL on one attempt does not mean the event was never delivered — check the newest attempt for that message. One of: `SUCCESS`, `PENDING`, `FAIL`, `SENDING`, `CANCELED`. | | `attempts.timestamp` | string | When this attempt was made. | | `attempts.trigger_type` | string (enum) | Why this attempt was made: TRIGGER_DELIVERY_ATTEMPT_TRIGGER_TYPE_SCHEDULED for the first attempt or an automatic retry, TRIGGER_DELIVERY_ATTEMPT_TRIGGER_TYPE_MANUAL for a retry someone started by hand. One of: `TRIGGER_DELIVERY_ATTEMPT_TRIGGER_TYPE_SCHEDULED`, `TRIGGER_DELIVERY_ATTEMPT_TRIGGER_TYPE_MANUAL`. | **Errors** - `400`: Invalid request - endpoint_id is missing, is longer than 256 characters or contains characters other than letters, digits, `.`, `_` and `-`, or does not name a trigger delivery endpoint in this environment - `404`: Not found - Triggers isn't turned on for your environment - `500`: Internal error - the delivery attempts could not be read; retry the request **Request** ```bash curl -sS -G -X GET \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/trigger-delivery/endpoints/ep_2qGv8XHk3mT5bJr9LwNc7pZeYfA/attempts" \ -H "Authorization: Bearer $TOKEN" \ --data-urlencode "page_size=20" ``` **Response** ```json { "attempts": [ { "id": "atmpt_2qGvA2bNf7KcW4xRm8TjP5sLhYd", "msg_id": "msg_2qGvA1sKd8RtY6nWm3LpX9cBhVe", "endpoint_id": "ep_2qGv8XHk3mT5bJr9LwNc7pZeYfA", "endpoint_url": "https://api.example.com/scalekit/triggers", "status": "SUCCESS", "response_status_code": 200, "response": "{\"ok\":true}", "response_truncated": false, "response_duration_ms": 84, "timestamp": "2026-10-28T09:17:45.020Z", "trigger_type": "TRIGGER_DELIVERY_ATTEMPT_TRIGGER_TYPE_SCHEDULED" } ] } ``` **Python SDK:** `scalekit_client.actions.triggers.list_delivery_attempts_by_endpoint` List the most recent attempts to deliver trigger events to one of your endpoints. ```python scalekit_client.actions.triggers.list_delivery_attempts_by_endpoint( endpoint_id: str, page_size: int | None = None, ) -> ListTriggerDeliveryAttemptsByEndpointResponse ``` Example: ```python result = scalekit_client.actions.triggers.list_delivery_attempts_by_endpoint( endpoint_id="ep_2qGv8XHk3mT5bJr9LwNc7pZeYfA", page_size=20, ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `endpoint_id` | `str` | Yes | ID of your delivery endpoint (`ep_...`), the URL on your server that you added under **Developers** > **Trigger Delivery**. [Look up a delivery](https://docs.scalekit.com/agentkit/reference/triggers/look-up-a-delivery/) returns it as `endpoint_id` on each attempt. | | `page_size` | `int \| None` | No | Attempts to return; the server default is 20, capped at 100. | Returns `ListTriggerDeliveryAttemptsByEndpointResponse`: The attempts (one page; there is no page token). ## Look up a delivery (beta) `GET /api/v1/trigger-delivery/deliveries/{id}` This endpoint is in beta. Its request and response may change. Finds one trigger event by an ID your server received: the message ID from the `webhook-id` header, or the event ID, which is the payload's `dedupe_key`. Returns the event's trigger, subscription and connection, and every attempt to deliver it to each of your endpoints, oldest first. The event's payload isn't returned. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | Yes | The message ID (`msg_...`, the `webhook-id` header) or the event ID (the payload's `dedupe_key`). | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `attempts` | array of object | The event's delivery attempts to every endpoint in this environment, oldest first. Each names the endpoint_id and endpoint_url it was sent to. | | `attempts.endpoint_id` | string | ID of the endpoint this attempt was made against. | | `attempts.endpoint_url` | string | URL this attempt was sent to. | | `attempts.id` | string | ID of this delivery attempt. | | `attempts.msg_id` | string | ID of the trigger event message this attempt delivered. Every attempt to deliver the same event shares this ID. | | `attempts.response` | string | Response body the endpoint returned for this attempt. A long body may be truncated. | | `attempts.response_duration_ms` | integer | How long the endpoint took to respond to this attempt, in milliseconds. | | `attempts.response_status_code` | integer | HTTP status code the endpoint returned for this attempt. | | `attempts.response_truncated` | boolean | True when `response` was cut to its first 4096 bytes. Set only by [List the attempts for a delivery](https://docs.scalekit.com/agentkit/reference/triggers/list-attempts-for-a-delivery/) and [Look up a delivery](https://docs.scalekit.com/agentkit/reference/triggers/look-up-a-delivery/), which apply that cap. | | `attempts.status` | string (enum) | Whether this attempt succeeded, is still in flight, failed, or was canceled. A failed delivery is retried, so FAIL on one attempt does not mean the event was never delivered — check the newest attempt for that message. One of: `SUCCESS`, `PENDING`, `FAIL`, `SENDING`, `CANCELED`. | | `attempts.timestamp` | string | When this attempt was made. | | `attempts.trigger_type` | string (enum) | Why this attempt was made: TRIGGER_DELIVERY_ATTEMPT_TRIGGER_TYPE_SCHEDULED for the first attempt or an automatic retry, TRIGGER_DELIVERY_ATTEMPT_TRIGGER_TYPE_MANUAL for a retry someone started by hand. One of: `TRIGGER_DELIVERY_ATTEMPT_TRIGGER_TYPE_SCHEDULED`, `TRIGGER_DELIVERY_ATTEMPT_TRIGGER_TYPE_MANUAL`. | | `connection_id` | string | ID of the connection the event came from. Present even after the connection is deleted. Empty when the event carries none. | | `connection_key_id` | string | Name of the connection the event came from, the value other AgentKit endpoints take as `connector`. Empty when the connection no longer exists. | | `connection_provider` | string | Provider of the connection the event came from. Empty when the connection no longer exists. | | `event_id` | string | Event ID of the event: the payload's `dedupe_key` and `correlation_id`. Empty when the event was sent without one. | | `message_timestamp` | string | When the event was accepted for delivery. | | `msg_id` | string | Message ID of the event, sent to each endpoint as the `webhook-id` header. | | `subscription_id` | string | ID of the trigger subscription that sent the event. Present even after the subscription is deleted. Empty when the event carries none. | | `trigger_mode` | string (enum) | How the trigger detects events, from the newest version of its definition: WEBHOOK or POLL. TRIGGER_MODE_UNSPECIFIED when the subscription or the definition no longer exists. One of: `WEBHOOK`, `POLL`. | | `trigger_name` | string | Key of the trigger that fired, as the subscription was created with. Empty when the subscription no longer exists. | | `truncated` | boolean | True when the event has more than the 50 attempts returned. The newest 50 are returned, so the earliest attempts are missing when this is true. | **Errors** - `400`: Invalid request - id is empty, longer than 256 characters, contains characters other than letters, digits, `.`, `_` and `-`, or is an attempt ID (`atmpt_`) - `404`: Not found - no trigger event with this message ID or event ID exists in this environment (error_code NOT_FOUND), or Triggers isn't turned on for your environment (error_code RESOURCE_NOT_FOUND) - `429`: Too many requests - deliveries are being read too often. Retry shortly; the request is not retried on your behalf - `500`: Internal error - the delivery could not be looked up; retry the request **Request** ```bash curl -sS -X GET \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/trigger-delivery/deliveries/msg_2qGvA1sKd8RtY6nWm3LpX9cBhVe" \ -H "Authorization: Bearer $TOKEN" ``` **Response** ```json { "msg_id": "msg_2qGvA1sKd8RtY6nWm3LpX9cBhVe", "event_id": "ca_5F133400349586228019.trgs_5F141020933185734981.00Q5g00000AbCdEFGH", "message_timestamp": "2026-10-28T09:17:44.950Z", "subscription_id": "trgs_141020933185734981", "trigger_name": "salesforce_lead_created", "trigger_mode": "POLL", "connection_id": "conn_133400101014995480", "connection_key_id": "salesforce", "connection_provider": "SALESFORCE", "attempts": [ { "id": "atmpt_2qGvA2bNf7KcW4xRm8TjP5sLhYd", "msg_id": "msg_2qGvA1sKd8RtY6nWm3LpX9cBhVe", "endpoint_id": "ep_2qGv8XHk3mT5bJr9LwNc7pZeYfA", "endpoint_url": "https://api.example.com/scalekit/triggers", "status": "SUCCESS", "response_status_code": 200, "response": "{\"ok\":true}", "response_truncated": false, "response_duration_ms": 84, "timestamp": "2026-10-28T09:17:45.020Z", "trigger_type": "TRIGGER_DELIVERY_ATTEMPT_TRIGGER_TYPE_SCHEDULED" } ], "truncated": false } ``` **Python SDK:** `scalekit_client.actions.triggers.lookup_delivery` Look up one delivered message by its message ID or by its event ID. ```python scalekit_client.actions.triggers.lookup_delivery( delivery_id: str, ) -> LookupTriggerDeliveryResponse ``` Example: ```python result = scalekit_client.actions.triggers.lookup_delivery( delivery_id="msg_2qGvA1sKd8RtY6nWm3LpX9cBhVe", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `delivery_id` | `str` | Yes | A message ID (`msg_...`) or the event ID your endpoint received (`TriggerEvent.dedupe_key`). | Returns `LookupTriggerDeliveryResponse`: The message, its trigger and up to 50 attempts. ## Look up several deliveries (beta) `POST /api/v1/trigger-delivery/deliveries:batchLookup` This endpoint is in beta. Its request and response may change. Finds up to 25 trigger events by message ID or event ID, in any mix, and returns a result for each, in the order sent. An ID that matches nothing comes back as `BATCH_LOOKUP_OUTCOME_NOT_FOUND`. Attempts here have no response body; [look up one delivery](https://docs.scalekit.com/agentkit/reference/triggers/look-up-a-delivery/) to read it. The call only reads, so retrying is safe. Up to 60 requests a minute per environment. **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `ids` | array of string | Yes | Message IDs (`msg_…`, sent to the endpoint as the `webhook-id` header) or event IDs (the payload's `dedupe_key`) of the trigger events to find, in any mix: at least 1 and at most 25, with no repeats. Each is 1 to 256 characters: letters, digits, `.`, `_` and `-`. An attempt ID (`atmpt_…`) refuses the whole request. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `found_count` | integer | How many of the requested IDs were found. | | `results` | array of object | One result per requested ID, in the order the IDs were sent, so a client can pair them up positionally. | | `results.delivery` | object | The event and its delivery attempts to every endpoint, oldest first, as [Look up a delivery](https://docs.scalekit.com/agentkit/reference/triggers/look-up-a-delivery/) returns them, except that no attempt carries a response body: each attempt's `response` is empty and its `response_truncated` is false. `truncated` keeps its meaning: true when the event has more than the 50 attempts returned. Present only for BATCH_LOOKUP_OUTCOME_FOUND. | | `results.delivery.attempts` | array of object | The event's delivery attempts to every endpoint in this environment, oldest first. Each names the endpoint_id and endpoint_url it was sent to. | | `results.delivery.connection_id` | string | ID of the connection the event came from. Present even after the connection is deleted. Empty when the event carries none. | | `results.delivery.connection_key_id` | string | Name of the connection the event came from, the value other AgentKit endpoints take as `connector`. Empty when the connection no longer exists. | | `results.delivery.connection_provider` | string | Provider of the connection the event came from. Empty when the connection no longer exists. | | `results.delivery.event_id` | string | Event ID of the event: the payload's `dedupe_key` and `correlation_id`. Empty when the event was sent without one. | | `results.delivery.message_timestamp` | string | When the event was accepted for delivery. | | `results.delivery.msg_id` | string | Message ID of the event, sent to each endpoint as the `webhook-id` header. | | `results.delivery.subscription_id` | string | ID of the trigger subscription that sent the event. Present even after the subscription is deleted. Empty when the event carries none. | | `results.delivery.trigger_mode` | string (enum) | How the trigger detects events, from the newest version of its definition: WEBHOOK or POLL. TRIGGER_MODE_UNSPECIFIED when the subscription or the definition no longer exists. One of: `WEBHOOK`, `POLL`. | | `results.delivery.trigger_name` | string | Key of the trigger that fired, as the subscription was created with. Empty when the subscription no longer exists. | | `results.delivery.truncated` | boolean | True when the event has more than the 50 attempts returned. The newest 50 are returned, so the earliest attempts are missing when this is true. | | `results.id` | string | The ID this result is about, exactly as sent. | | `results.outcome` | string (enum) | What happened to this ID. BATCH_LOOKUP_OUTCOME_FOUND — the event was found and delivery holds it. BATCH_LOOKUP_OUTCOME_NOT_FOUND — no trigger event with this message ID or event ID exists in this environment. BATCH_LOOKUP_OUTCOME_RATE_LIMITED — the read was refused for volume; look the ID up again shortly. BATCH_LOOKUP_OUTCOME_FAILED — the lookup could not be completed; retrying may succeed. BATCH_LOOKUP_OUTCOME_NOT_ATTEMPTED — not looked up, because an earlier ID was rate limited or the request ran out of time; send it again. One of: `BATCH_LOOKUP_OUTCOME_FOUND`, `BATCH_LOOKUP_OUTCOME_NOT_FOUND`, `BATCH_LOOKUP_OUTCOME_RATE_LIMITED`, `BATCH_LOOKUP_OUTCOME_FAILED`, `BATCH_LOOKUP_OUTCOME_NOT_ATTEMPTED`. | | `results.reason` | string | One sentence explaining the outcome. Empty for BATCH_LOOKUP_OUTCOME_FOUND. Never contains internal error detail. The wording can change: match on outcome, not on this text. | **Errors** - `400`: Invalid request, nothing looked up - `ids` is empty, holds more than 25 IDs or repeats one; an ID is longer than 256 characters or contains characters other than letters, digits, `.`, `_` and `-`; or an ID is an attempt ID (`atmpt_`), which [Look up a delivery](https://docs.scalekit.com/agentkit/reference/triggers/look-up-a-delivery/) refuses too. A well-formed ID that names no trigger event in this environment is not this case: it comes back as a NOT_FOUND result with a 200 - `404`: Not found - Triggers isn't turned on for your environment. An ID that names no trigger event is not this case: it is a NOT_FOUND result with a 200 - `429`: Too many requests - this environment has sent too many batch lookup requests in the last minute. Nothing was looked up; retry after a short wait. Reads refused for volume partway through a batch are not this case: they are reported per ID as RATE_LIMITED, with a 200 - `500`: Internal error - the environment's trigger deliveries could not be reached, so nothing was looked up; retry the request **Request** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/trigger-delivery/deliveries:batchLookup" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "ids": [ "msg_2qGvA1sKd8RtY6nWm3LpX9cBhVe", "msg_2qGvA9zZz0000000000000000" ] }' ``` **Response** ```json { "results": [ { "id": "msg_2qGvA1sKd8RtY6nWm3LpX9cBhVe", "outcome": "BATCH_LOOKUP_OUTCOME_FOUND", "reason": "", "delivery": { "msg_id": "msg_2qGvA1sKd8RtY6nWm3LpX9cBhVe", "event_id": "ca_5F133400349586228019.trgs_5F141020933185734981.00Q5g00000AbCdEFGH", "message_timestamp": "2026-10-28T09:17:44.950Z", "subscription_id": "trgs_141020933185734981", "trigger_name": "salesforce_lead_created", "trigger_mode": "POLL", "connection_id": "conn_133400101014995480", "connection_key_id": "salesforce", "connection_provider": "SALESFORCE", "attempts": [ { "id": "atmpt_2qGvA2bNf7KcW4xRm8TjP5sLhYd", "msg_id": "msg_2qGvA1sKd8RtY6nWm3LpX9cBhVe", "endpoint_id": "ep_2qGv8XHk3mT5bJr9LwNc7pZeYfA", "endpoint_url": "https://api.example.com/scalekit/triggers", "status": "SUCCESS", "response_status_code": 200, "response": "", "response_truncated": false, "response_duration_ms": 84, "timestamp": "2026-10-28T09:17:45.020Z", "trigger_type": "TRIGGER_DELIVERY_ATTEMPT_TRIGGER_TYPE_SCHEDULED" } ], "truncated": false } }, { "id": "msg_2qGvA9zZz0000000000000000", "outcome": "BATCH_LOOKUP_OUTCOME_NOT_FOUND", "reason": "no trigger delivery with this id exists in this environment" } ], "found_count": 1 } ``` **Python SDK:** `scalekit_client.actions.triggers.batch_lookup_deliveries` Look up 1 to 25 delivered messages by message ID or event ID. Each ID gets its own outcome in `results` (`FOUND`, `NOT_FOUND`, `RATE_LIMITED`, `FAILED`, `NOT_ATTEMPTED`), and those never raise. Attempts in the results carry no response bodies. ```python scalekit_client.actions.triggers.batch_lookup_deliveries( delivery_ids: Sequence[str], ) -> BatchLookupTriggerDeliveriesResponse ``` Example: ```python result = scalekit_client.actions.triggers.batch_lookup_deliveries( delivery_ids=["msg_2qGvA1sKd8RtY6nWm3LpX9cBhVe", "msg_2qGvA9zZz0000000000000000"], ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `delivery_ids` | `Sequence[str]` | Yes | Message IDs (`msg_...`) or event IDs, unique. | Returns `BatchLookupTriggerDeliveriesResponse`: Per-ID results and `found_count`. ## Resend deliveries (beta) `POST /api/v1/trigger-delivery/deliveries:batchResend` This endpoint is in beta. Its request and response may change. Queues one more attempt to deliver each event to the endpoint you name, for up to 50 events. `BATCH_RESEND_OUTCOME_QUEUED` means the attempt was queued, not that it succeeded; check it with [List the attempts for a delivery](https://docs.scalekit.com/agentkit/reference/triggers/list-attempts-for-a-delivery/). Each call queues another attempt, so after a partial failure resend only the events that didn't come back `QUEUED`. Up to 60 requests a minute per environment. **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `targets` | array of object | Yes | The deliveries to retry, each a message ID and an endpoint ID: at least 1 and at most 50. A msg_id and endpoint_id pair may appear only once; a repeated pair refuses the whole request rather than queueing two attempts. The same message may appear with different endpoints, and the same endpoint with different messages. | | `targets.endpoint_id` | string | Yes | ID of your delivery endpoint (`ep_...`), the URL on your server that you added under **Developers** > **Trigger Delivery**. [Look up a delivery](https://docs.scalekit.com/agentkit/reference/triggers/look-up-a-delivery/) returns it as `endpoint_id` on each attempt. | | `targets.msg_id` | string | Yes | Message ID of the trigger event to deliver again, as returned in msg_id by [List deliveries](https://docs.scalekit.com/agentkit/reference/triggers/list-deliveries/): `msg_` followed by 1 to 64 letters or digits. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `queued_count` | integer | How many new delivery attempts this call queued. | | `results` | array of object | One result per target, in the order the targets were sent, so a client can pair them up positionally. | | `results.endpoint_id` | string | Endpoint ID of the target, exactly as sent. | | `results.msg_id` | string | Message ID of the target, exactly as sent. | | `results.outcome` | string (enum) | What happened to this target. BATCH_RESEND_OUTCOME_QUEUED — a new manual attempt was queued; it has not been delivered yet. BATCH_RESEND_OUTCOME_NOT_FOUND — the message or the endpoint does not exist in this environment. BATCH_RESEND_OUTCOME_RATE_LIMITED — refused for volume; send this target again shortly. BATCH_RESEND_OUTCOME_FAILED — no new attempt could be confirmed as queued; one whose request timed out may still have been, so a retry can queue a second attempt. A target whose event payload is no longer stored is also FAILED, with a reason saying it cannot be resent; retrying it will not help. BATCH_RESEND_OUTCOME_NOT_ATTEMPTED — not sent, because an earlier target was rate limited or the request ran out of time; nothing was queued for it. One of: `BATCH_RESEND_OUTCOME_QUEUED`, `BATCH_RESEND_OUTCOME_NOT_FOUND`, `BATCH_RESEND_OUTCOME_RATE_LIMITED`, `BATCH_RESEND_OUTCOME_FAILED`, `BATCH_RESEND_OUTCOME_NOT_ATTEMPTED`. | | `results.reason` | string | One sentence explaining the outcome. Empty for BATCH_RESEND_OUTCOME_QUEUED. Never contains internal error detail. The wording can change: match on outcome, not on this text. | **Errors** - `400`: Invalid request, nothing queued - `targets` is empty or holds more than 50; a `msg_id` is not a message ID (`msg_` followed by 1 to 64 letters or digits) or an `endpoint_id` is not an endpoint ID (`ep_` followed by 1 to 64 letters or digits); or the same `msg_id` and `endpoint_id` pair appears twice, since sending it twice would queue two attempts. A well-formed message or endpoint ID that does not exist in this environment is not this case: it comes back as a NOT_FOUND result with a 200 - `404`: Not found - Triggers isn't turned on for your environment. Nothing was queued. A message or endpoint that does not exist is not this case: it is a NOT_FOUND result with a 200 - `429`: Too many requests - this environment has sent too many batch resend requests in the last minute. Nothing was queued; retry after a short wait. A rate limit reached partway through a batch is not this case: it is reported per target as RATE_LIMITED, with a 200 - `500`: Internal error - the environment's trigger deliveries could not be reached, so nothing was queued; retry the request **Request** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/trigger-delivery/deliveries:batchResend" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "targets": [ { "msg_id": "msg_2qGvA4uPf2HjQ7zTn5KrW8dCxLb", "endpoint_id": "ep_2qGv8XHk3mT5bJr9LwNc7pZeYfA" } ] }' ``` **Response** ```json { "results": [ { "msg_id": "msg_2qGvA4uPf2HjQ7zTn5KrW8dCxLb", "endpoint_id": "ep_2qGv8XHk3mT5bJr9LwNc7pZeYfA", "outcome": "BATCH_RESEND_OUTCOME_QUEUED", "reason": "" } ], "queued_count": 1 } ``` **Python SDK:** `scalekit_client.actions.triggers.batch_resend_deliveries` Send 1 to 50 messages to endpoints again. Each target makes a real delivery attempt and gets its own outcome in `results` (`QUEUED`, `NOT_FOUND`, `RATE_LIMITED`, `FAILED`, `NOT_ATTEMPTED`), which never raise. Not retried on `UNAVAILABLE`: a retry could deliver the messages twice. ```python scalekit_client.actions.triggers.batch_resend_deliveries( targets: Sequence[TriggerDeliveryResendTarget | TriggerDeliveryResendTargetParam], ) -> BatchResendTriggerDeliveriesResponse ``` Example: ```python result = scalekit_client.actions.triggers.batch_resend_deliveries( targets=[{"msg_id": "msg_2qGvA4uPf2HjQ7zTn5KrW8dCxLb", "endpoint_id": "ep_2qGv8XHk3mT5bJr9LwNc7pZeYfA"}], ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `targets` | `Sequence[TriggerDeliveryResendTarget \| TriggerDeliveryResendTargetParam]` | Yes | `TriggerDeliveryResendTarget` models or `{"msg_id": "msg_...", "endpoint_id": "ep_..."}` dicts. | Returns `BatchResendTriggerDeliveriesResponse`: Per-target results and `queued_count`. ## Recover failed deliveries (beta) `POST /api/v1/trigger-delivery/endpoints/{endpoint_id}:recover` This endpoint is in beta. Its request and response may change. Resends every event whose delivery to this endpoint failed between `since` and `until`, once your endpoint is working again. Leave out `until` to recover up to now. The window is at most 14 days, and one call recovers at most 10,000 events, so cover a long outage with several narrower windows. The recovery runs in the background; the response only confirms it started. Calling again resends events that are still failed. Up to 6 requests a minute per environment. **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `endpoint_id` | string | Yes | ID of your delivery endpoint (`ep_...`), the URL on your server that you added under **Developers** > **Trigger Delivery**. [Look up a delivery](https://docs.scalekit.com/agentkit/reference/triggers/look-up-a-delivery/) returns it as `endpoint_id` on each attempt. | **Request body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `since` | string | Yes | Start of the window, inclusive: events with a failed attempt to this endpoint at or after this instant are recovered. Must not be in the future, and must be at most 14 days before `until`, or before now when `until` is omitted. | | `until` | string | No | End of the window, exclusive. Omit it to recover up to now. Must be after `since`, must not be in the future, and must be at most 14 days after `since`. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `status` | string | The recovery's state when this call returned. `running` in practice: the recovery continues after the response, and its later state is not reported. | | `task_id` | string | Identifies this recovery in Scalekit's logs; quote it when contacting support. It cannot be looked up or polled through the API. | **Errors** - `400`: Invalid request, no recovery started - `endpoint_id` is not an endpoint ID (`ep_` followed by 1 to 64 letters or digits); `since` is missing, or `since` or `until` is not a valid timestamp; `since` is in the future; `until` is in the future; `until` is not after `since`; or `until` is more than 14 days after `since`, measured to now when `until` is omitted, so the period must be split into several calls - `404`: Not found - the endpoint is not one of this environment's trigger delivery endpoints, or Triggers isn't turned on for your environment. No recovery started - `429`: Too many requests - this environment has started too many recoveries in a short period, or requests are being refused for volume. No recovery started; retry after a short wait - `500`: Internal error - the recovery was not confirmed. Either it could not be started, or the request timed out or failed after it may already have been accepted, in which case the error message says it may be running. In that case look for new manual attempts with [List deliveries](https://docs.scalekit.com/agentkit/reference/triggers/list-deliveries/) before calling again, because a second call can queue a second attempt for the same event **Request** ```bash curl -sS -X POST \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/trigger-delivery/endpoints/ep_2qGv8XHk3mT5bJr9LwNc7pZeYfA:recover" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "since": "2026-10-28T09:00:00Z" }' ``` **Response** ```json { "task_id": "qtask_2qGvB3mKd9XcV1nTq6RpW4sLhZe", "status": "running" } ``` **Python SDK:** `scalekit_client.actions.triggers.recover_deliveries` Resend every message that failed to reach an endpoint in a time range. Not retried on `UNAVAILABLE`. A `ScalekitInternalServerException` here means the recovery may be running anyway: check the endpoint's deliveries before calling again. ```python scalekit_client.actions.triggers.recover_deliveries( endpoint_id: str, since: datetime, until: datetime | None = None, ) -> RecoverTriggerDeliveriesResponse ``` Example: ```python from datetime import datetime, timezone result = scalekit_client.actions.triggers.recover_deliveries( endpoint_id="ep_2qGv8XHk3mT5bJr9LwNc7pZeYfA", since=datetime(2026, 10, 28, 9, 0, tzinfo=timezone.utc), ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `endpoint_id` | `str` | Yes | ID of your delivery endpoint (`ep_...`), the URL on your server that you added under **Developers** > **Trigger Delivery**. [Look up a delivery](https://docs.scalekit.com/agentkit/reference/triggers/look-up-a-delivery/) returns it as `endpoint_id` on each attempt. | | `since` | `datetime` | Yes | Start of the range, an aware instant not in the future. | | `until` | `datetime \| None` | No | End of the range (default now), at most 14 days after `since`. | Returns `RecoverTriggerDeliveriesResponse`: The recovery task and its status (`"running"` or `"finished"`). ## Get trigger activity (beta) `GET /api/v1/trigger-activity` This endpoint is in beta. Its request and response may change. Counts what happened to the environment's trigger events in the last 24 hours, by outcome, across every connection. Use it to find why a trigger didn't fire: the counts tell apart events that never arrived, events that matched no subscription, and events that failed signature verification. They're totals only; to follow one event, [look up its delivery](https://docs.scalekit.com/agentkit/reference/triggers/look-up-a-delivery/). **Response (200)** | Name | Type | Description | | --- | --- | --- | | `counts` | object | Event counts over the window, keyed by outcome. Every key is always present, zeroes included. received — events that arrived at one of the environment's webhook URLs, including those that then failed signature verification. filtered — verified events that no subscription's filter or account matched. unresolved — verified events for a provider workspace that no connected account holds. delivered — events handed off for delivery to your endpoints; this does not confirm that your endpoint received them. delivery_failed — events whose handoff for delivery failed. duplicate — repeated deliveries of an event already received, such as provider retries, which are ignored. signature_failed — events that failed signature verification. discarded — events received while Triggers wasn't turned on for your environment, and dropped. | | `degraded` | boolean | True when some counts could not be read, so the counts are incomplete. Do not read zero counts as "nothing arrived" while this is true. | | `delivery_details_path` | string | Path, relative to the Scalekit dashboard, of the trigger delivery page. That page shows each delivery attempt to your endpoints, including failures after an event was handed off for delivery. | | `explains_individual_events` | boolean | Always false. Trigger events are not stored individually, so no endpoint can explain what happened to one specific event. | | `signature_banner` | boolean | True when at least 5 events in the window failed signature verification and they make up at least half of the events received. This usually means the signing secret set on a connection does not match the provider's. Show it prominently: a provider can disable a webhook URL that keeps failing, and re-enabling it is a manual step in the provider's app configuration. | | `window_hours` | integer | How many hours back the counts reach, ending now. | **Errors** - `404`: Not found - Triggers isn't turned on for your environment - `500`: Internal error - the activity counts could not be read; retry the request **Request** ```bash curl -sS -X GET \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/trigger-activity" \ -H "Authorization: Bearer $TOKEN" ``` **Response** ```json { "window_hours": 24, "counts": { "received": "45", "filtered": "6", "unresolved": "0", "delivered": "39", "delivery_failed": "0", "duplicate": "2", "signature_failed": "0", "discarded": "0" }, "signature_banner": false, "degraded": false, "explains_individual_events": false, "delivery_details_path": "/trigger-delivery" } ``` **Python SDK:** `scalekit_client.actions.triggers.get_activity` Count what happened to this environment's trigger events recently. ```python scalekit_client.actions.triggers.get_activity() -> GetTriggerActivityResponse ``` Example: ```python result = scalekit_client.actions.triggers.get_activity() ``` Returns `GetTriggerActivityResponse`: Counts per outcome over `window`, plus signature and degraded flags. ## Get trigger usage (beta) `GET /api/v1/trigger-usage` This endpoint is in beta. Its request and response may change. Gets the trigger deliveries metered for your environment. One unit is one event sent to one of your endpoints. Ask for `period` (`24h` by the hour, `7d` or `30d` by the UTC day), or for a range of UTC dates with `from` and `to`, up to 30 days back. Hours and days with no usage are left out, so fill the gaps with zero. **Query parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `from` | string | No | First UTC calendar date to include, as YYYY-MM-DD. Inclusive. Required when `period` is not sent, and must be left empty when it is. Must be a real date, must not be after `to`, and must not be after today's UTC date. A date more than 30 days before today's UTC date is clamped to the first day of the readable window, 30 days before today. | | `period` | string (enum) | No | A preset period ending now, sent instead of `from` and `to`: exactly `24h`, `7d` or `30d`, lowercase. `24h` reads hourly counts for the 24 UTC clock hours ending with the hour in progress. `7d` and `30d` read daily counts for the UTC calendar days ending with today's UTC date: the week ending today, or the 30 days ending today. Leave `from` and `to` empty when this is set; when it is not, both are required. One of: `24h`, `7d`, `30d`. | | `to` | string | No | Last UTC calendar date to include, as YYYY-MM-DD. Inclusive. Required when `period` is not sent, and must be left empty when it is. Must be a real date and must not be more than 30 days before today's UTC date. A date after today's UTC date is clamped to today. | **Response (200)** | Name | Type | Description | | --- | --- | --- | | `bucket` | string (enum) | What each entry counts. `TRIGGER_USAGE_BUCKET_HOUR` for `period=24h`, whose counts are in `hours`. `TRIGGER_USAGE_BUCKET_DAY` for `period=7d` and `period=30d`, whose counts are in `days`. `TRIGGER_USAGE_BUCKET_UNSPECIFIED` when the request used `from` and `to`; those counts are always by UTC day, in `days`. One of: `TRIGGER_USAGE_BUCKET_DAY`, `TRIGGER_USAGE_BUCKET_HOUR`. | | `current_hour` | string | Only for `period=24h`, and empty otherwise. The start of the UTC clock hour in progress on the server, as an RFC 3339 timestamp in UTC (`Z`). It is the last of the 24 hours `hours` covers, and its count grows until the hour ends. Returned so a client lays out the 24 hours and marks the one in progress without relying on its own clock. | | `days` | array of object | One entry per UTC day within the range read (`from`..`to`) that has metered deliveries, oldest first. A day in that range with none is absent rather than listed as zero, so a client that needs a continuous series fills the missing days itself, over the returned `from` and `to` rather than the range it requested. Days outside the range read were not read and must not be drawn as zero. Always empty for `period=24h`, whose counts are in `hours`. | | `days.date` | string | The UTC calendar date, as YYYY-MM-DD. | | `days.units` | string | Trigger deliveries metered for this environment on this day: one for each trigger event sent to one receiving endpoint. Always greater than zero; a day with none is left out of the response. | | `from` | string | First UTC calendar date of the range read, as YYYY-MM-DD. For a `from` and `to` request, the requested `from`, or the first day of the readable window, 30 days before `today`, when the request started earlier. For a `period` request, the UTC date of the first hour (`24h`) or the first day (`7d`, `30d`) of the period. | | `hours` | array of object | Only for `period=24h`, and empty otherwise. One entry per UTC clock hour that has metered deliveries, oldest first, among the 24 hours from 23 hours before `current_hour` through `current_hour`. An hour in that range with none is absent rather than listed as zero, so a client that needs a continuous series fills the missing hours itself over those 24 hours. Hours outside them were not read and must not be drawn. | | `hours.start` | string | The start of the UTC clock hour, as an RFC 3339 timestamp in UTC (`Z`) with zero minutes and seconds. The hour runs from `start` up to, not including, `start` plus one hour. | | `hours.units` | string | Trigger deliveries metered for this environment in this hour: one for each trigger event sent to one receiving endpoint. Always greater than zero; an hour with none is left out of the response. | | `period` | string | The `period` the request asked for: `24h`, `7d` or `30d`. Empty when the request used `from` and `to`. | | `to` | string | Last UTC calendar date of the range read, as YYYY-MM-DD. For a `from` and `to` request, the requested `to`, or `today` when the request ended later. For a `period` request, always `today`. | | `today` | string | The server's current UTC calendar date, as YYYY-MM-DD. Deliveries on this day are still being metered, so its count grows until the day ends. The readable window and any clamping of `from` and `to` are measured from this date. Returned so a client tells the day in progress apart from completed days without relying on its own clock. | **Errors** - `400`: Invalid request - `period` is not exactly `24h`, `7d` or `30d`; `period` is sent together with `from` or `to`; `period` is not sent and `from` or `to` is missing; a date is not a real calendar date in YYYY-MM-DD form; `from` is after `to`; `from` is after today's UTC date; or `to` is more than 30 days before today's UTC date. A range that only extends past the readable window is clamped, not refused - `404`: Not found - trigger usage is not metered for this environment (error_code TRIGGER_USAGE_NOT_METERED), or Triggers isn't turned on for your environment (error_code RESOURCE_NOT_FOUND) - `500`: Internal error - the usage could not be read; retry the request - `503`: Service unavailable - usage data could not be read right now. No partial figures are returned; retry the request later **Request** ```bash curl -sS -G -X GET \ "$SCALEKIT_ENVIRONMENT_URL/api/v1/trigger-usage" \ -H "Authorization: Bearer $TOKEN" \ --data-urlencode "period=7d" ``` **Response** ```json { "period": "7d", "bucket": "TRIGGER_USAGE_BUCKET_DAY", "from": "2026-10-22", "to": "2026-10-28", "today": "2026-10-28", "days": [ { "date": "2026-10-27", "units": "112" }, { "date": "2026-10-28", "units": "39" } ], "hours": [], "current_hour": "" } ``` **Python SDK:** `scalekit_client.actions.triggers.get_usage` Read metered trigger deliveries for a preset period or a UTC date range. Pass either `period` or both `from_date` and `to_date`. The server checks the combination and clamps the range to the last 30 days. ```python scalekit_client.actions.triggers.get_usage( period: TriggerUsagePeriod | str | None = None, from_date: date | str | None = None, to_date: date | str | None = None, ) -> GetTriggerUsageResponse ``` Example: ```python result = scalekit_client.actions.triggers.get_usage( period="7d", ) ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `period` | `TriggerUsagePeriod \| str \| None` | No | `"24h"` (hourly counts) or `"7d"`/`"30d"` (daily counts). | | `from_date` | `date \| str \| None` | No | First UTC date, inclusive: a `date`, an aware `datetime` (its UTC date is used) or a `"YYYY-MM-DD"` string. | | `to_date` | `date \| str \| None` | No | Last UTC date, inclusive, in the same forms. | Returns `GetTriggerUsageResponse`: Daily or hourly counts and the range actually read. --- Source: https://docs.scalekit.com/agentkit/reference/events.md # Webhooks Scalekit sends these webhooks when a connected account changes. [Listen for account events](https://docs.scalekit.com/agentkit/account-events/) shows how to add an endpoint and verify each request. One page per endpoint, each with its own markdown copy: - [connected_account.created](https://docs.scalekit.com/agentkit/reference/events/connected-account-created.md) - [connected_account.updated](https://docs.scalekit.com/agentkit/reference/events/connected-account-updated.md) - [connected_account.deleted](https://docs.scalekit.com/agentkit/reference/events/connected-account-deleted.md) - [connected_account.magic_link_generated](https://docs.scalekit.com/agentkit/reference/events/connected-account-magic-link-generated.md) - [connected_account.oauth_tokens_fetched](https://docs.scalekit.com/agentkit/reference/events/connected-account-oauth-tokens-fetched.md) - [connected_account.oauth_succeeded](https://docs.scalekit.com/agentkit/reference/events/connected-account-oauth-succeeded.md) - [connected_account.token_refresh_succeeded](https://docs.scalekit.com/agentkit/reference/events/connected-account-token-refresh-succeeded.md) - [connected_account.token_refresh_failed](https://docs.scalekit.com/agentkit/reference/events/connected-account-token-refresh-failed.md) - [connected_account.status_updated](https://docs.scalekit.com/agentkit/reference/events/connected-account-status-updated.md) ## connected_account.created Triggered when a new connected account is created Scalekit sends the event as a JSON `POST` to your webhook endpoint, signed in the `webhook-id`, `webhook-timestamp` and `webhook-signature` headers. Verify the signature before you act on it; see [Listen for account events](https://docs.scalekit.com/agentkit/account-events/). **Payload** | Name | Type | Description | | --- | --- | --- | | `data` | object | The connected account the event is about. | | `data.connection_id` | string | The connection's ID. | | `data.id` | string | The connected account's ID. | | `data.identifier` | string | Your app's ID for the user, the value passed when the account was created. | | `data.provider` | string | The app, such as `GMAIL`. | | `data.status` | string (enum) | Current connected account status. One of: `ACTIVE`, `EXPIRED`, `PENDING_AUTH`, `PENDING_VERIFICATION`, `DISCONNECTED`. | | `data.authorization_type` | string (enum) | Authorization type of the connected account. One of: `OAUTH`, `API_KEY`, `BASIC_AUTH`, `BEARER_TOKEN`, `CUSTOM`, `BASIC`, `OAUTH_M2M`, `TRELLO_OAUTH1`, `GOOGLE_DWD`, `TRUSTED_IDP`, `SMART_FHIR`, `NO_AUTH`. | | `data.connection_name` | string | The connection's name, as shown in AgentKit > Connections. Omitted when it can't be resolved. | | `data.last_used_at` | string | When the connected account was last used. | | `data.token_expires_at` | string | When the access token expires, if known. | | `environment_id` | string | The environment ID where the event occurred. | | `id` | string | Unique identifier for the webhook event (must be prefixed with "evt_"). | | `object` | string (enum) | The type of object that triggered the webhook. One of: `ConnectedAccount`. | | `occurred_at` | string | When the event occurred (ISO 8601 format). | | `spec_version` | string | The webhook specification version. | | `type` | string (enum) | The event type. One of: `connected_account.created`. | | `organization_id` | string | The organization ID (if applicable). | **Example** ```json { "spec_version": "1", "id": "evt_101652975398683150", "type": "connected_account.created", "occurred_at": "2026-10-02T14:30:00.512Z", "environment_id": "env_88640229614813449", "object": "ConnectedAccount", "data": { "id": "ca_24834495392086178", "identifier": "user_123", "connection_id": "conn_70219645518265104", "connection_name": "gmail", "provider": "GMAIL", "authorization_type": "OAUTH", "status": "PENDING_AUTH" } } ``` ## connected_account.updated Triggered when a connected account is updated Scalekit sends the event as a JSON `POST` to your webhook endpoint, signed in the `webhook-id`, `webhook-timestamp` and `webhook-signature` headers. Verify the signature before you act on it; see [Listen for account events](https://docs.scalekit.com/agentkit/account-events/). **Payload** | Name | Type | Description | | --- | --- | --- | | `data` | object | The connected account the event is about. | | `data.connection_id` | string | The connection's ID. | | `data.id` | string | The connected account's ID. | | `data.identifier` | string | Your app's ID for the user, the value passed when the account was created. | | `data.provider` | string | The app, such as `GMAIL`. | | `data.status` | string (enum) | Current connected account status. One of: `ACTIVE`, `EXPIRED`, `PENDING_AUTH`, `PENDING_VERIFICATION`, `DISCONNECTED`. | | `data.authorization_type` | string (enum) | Authorization type of the connected account. One of: `OAUTH`, `API_KEY`, `BASIC_AUTH`, `BEARER_TOKEN`, `CUSTOM`, `BASIC`, `OAUTH_M2M`, `TRELLO_OAUTH1`, `GOOGLE_DWD`, `TRUSTED_IDP`, `SMART_FHIR`, `NO_AUTH`. | | `data.connection_name` | string | The connection's name, as shown in AgentKit > Connections. Omitted when it can't be resolved. | | `data.last_used_at` | string | When the connected account was last used. | | `data.token_expires_at` | string | When the access token expires, if known. | | `environment_id` | string | The environment ID where the event occurred. | | `id` | string | Unique identifier for the webhook event (must be prefixed with "evt_"). | | `object` | string (enum) | The type of object that triggered the webhook. One of: `ConnectedAccount`. | | `occurred_at` | string | When the event occurred (ISO 8601 format). | | `spec_version` | string | The webhook specification version. | | `type` | string (enum) | The event type. One of: `connected_account.updated`. | | `organization_id` | string | The organization ID (if applicable). | **Example** ```json { "spec_version": "1", "id": "evt_101652975398683154", "type": "connected_account.updated", "occurred_at": "2026-10-02T14:34:00.512Z", "environment_id": "env_88640229614813449", "object": "ConnectedAccount", "data": { "id": "ca_24834495392086178", "identifier": "user_123", "connection_id": "conn_70219645518265104", "connection_name": "gmail", "provider": "GMAIL", "authorization_type": "OAUTH", "status": "ACTIVE", "token_expires_at": "2026-10-02T15:30:00Z" } } ``` ## connected_account.deleted Triggered when a connected account is deleted Scalekit sends the event as a JSON `POST` to your webhook endpoint, signed in the `webhook-id`, `webhook-timestamp` and `webhook-signature` headers. Verify the signature before you act on it; see [Listen for account events](https://docs.scalekit.com/agentkit/account-events/). **Payload** | Name | Type | Description | | --- | --- | --- | | `data` | object | The connected account the event is about. | | `data.connection_id` | string | The connection's ID. | | `data.id` | string | The connected account's ID. | | `data.identifier` | string | Your app's ID for the user, the value passed when the account was created. | | `data.provider` | string | The app, such as `GMAIL`. | | `data.status` | string (enum) | Current connected account status. One of: `ACTIVE`, `EXPIRED`, `PENDING_AUTH`, `PENDING_VERIFICATION`, `DISCONNECTED`. | | `data.authorization_type` | string (enum) | Authorization type of the connected account. One of: `OAUTH`, `API_KEY`, `BASIC_AUTH`, `BEARER_TOKEN`, `CUSTOM`, `BASIC`, `OAUTH_M2M`, `TRELLO_OAUTH1`, `GOOGLE_DWD`, `TRUSTED_IDP`, `SMART_FHIR`, `NO_AUTH`. | | `data.connection_name` | string | The connection's name, as shown in AgentKit > Connections. Omitted when it can't be resolved. | | `data.last_used_at` | string | When the connected account was last used. | | `data.token_expires_at` | string | When the access token expires, if known. | | `environment_id` | string | The environment ID where the event occurred. | | `id` | string | Unique identifier for the webhook event (must be prefixed with "evt_"). | | `object` | string (enum) | The type of object that triggered the webhook. One of: `ConnectedAccount`. | | `occurred_at` | string | When the event occurred (ISO 8601 format). | | `spec_version` | string | The webhook specification version. | | `type` | string (enum) | The event type. One of: `connected_account.deleted`. | | `organization_id` | string | The organization ID (if applicable). | **Example** ```json { "spec_version": "1", "id": "evt_101652975398683157", "type": "connected_account.deleted", "occurred_at": "2026-10-02T14:37:00.512Z", "environment_id": "env_88640229614813449", "object": "ConnectedAccount", "data": { "id": "ca_24834495392086178", "identifier": "user_123", "connection_id": "conn_70219645518265104", "connection_name": "gmail", "provider": "GMAIL", "authorization_type": "OAUTH", "status": "ACTIVE", "token_expires_at": "2026-10-02T15:30:00Z" } } ``` ## connected_account.magic_link_generated Triggered when an authorization link is created for a user Scalekit sends the event as a JSON `POST` to your webhook endpoint, signed in the `webhook-id`, `webhook-timestamp` and `webhook-signature` headers. Verify the signature before you act on it; see [Listen for account events](https://docs.scalekit.com/agentkit/account-events/). **Payload** | Name | Type | Description | | --- | --- | --- | | `data` | object | The connected account the event is about. | | `data.connection_id` | string | The connection's ID. | | `data.id` | string | The connected account's ID. | | `data.identifier` | string | Your app's ID for the user, the value passed when the account was created. | | `data.provider` | string | The app, such as `GMAIL`. | | `data.status` | string (enum) | Current connected account status. One of: `ACTIVE`, `EXPIRED`, `PENDING_AUTH`, `PENDING_VERIFICATION`, `DISCONNECTED`. | | `data.authorization_type` | string (enum) | Authorization type of the connected account. One of: `OAUTH`, `API_KEY`, `BASIC_AUTH`, `BEARER_TOKEN`, `CUSTOM`, `BASIC`, `OAUTH_M2M`, `TRELLO_OAUTH1`, `GOOGLE_DWD`, `TRUSTED_IDP`, `SMART_FHIR`, `NO_AUTH`. | | `data.connection_name` | string | The connection's name, as shown in AgentKit > Connections. Omitted when it can't be resolved. | | `data.last_used_at` | string | When the connected account was last used. | | `data.token_expires_at` | string | When the access token expires, if known. | | `environment_id` | string | The environment ID where the event occurred. | | `id` | string | Unique identifier for the webhook event (must be prefixed with "evt_"). | | `object` | string (enum) | The type of object that triggered the webhook. One of: `ConnectedAccount`. | | `occurred_at` | string | When the event occurred (ISO 8601 format). | | `spec_version` | string | The webhook specification version. | | `type` | string (enum) | The event type. One of: `connected_account.magic_link_generated`. | | `organization_id` | string | The organization ID (if applicable). | **Example** ```json { "spec_version": "1", "id": "evt_101652975398683151", "type": "connected_account.magic_link_generated", "occurred_at": "2026-10-02T14:31:00.512Z", "environment_id": "env_88640229614813449", "object": "ConnectedAccount", "data": { "id": "ca_24834495392086178", "identifier": "user_123", "connection_id": "conn_70219645518265104", "connection_name": "gmail", "provider": "GMAIL", "authorization_type": "OAUTH", "status": "PENDING_AUTH" } } ``` ## connected_account.oauth_tokens_fetched Triggered when OAuth tokens are successfully fetched Scalekit sends the event as a JSON `POST` to your webhook endpoint, signed in the `webhook-id`, `webhook-timestamp` and `webhook-signature` headers. Verify the signature before you act on it; see [Listen for account events](https://docs.scalekit.com/agentkit/account-events/). **Payload** | Name | Type | Description | | --- | --- | --- | | `data` | object | The connected account the event is about. | | `data.connection_id` | string | The connection's ID. | | `data.id` | string | The connected account's ID. | | `data.identifier` | string | Your app's ID for the user, the value passed when the account was created. | | `data.provider` | string | The app, such as `GMAIL`. | | `data.status` | string (enum) | Current connected account status. One of: `ACTIVE`, `EXPIRED`, `PENDING_AUTH`, `PENDING_VERIFICATION`, `DISCONNECTED`. | | `data.authorization_type` | string (enum) | Authorization type of the connected account. One of: `OAUTH`, `API_KEY`, `BASIC_AUTH`, `BEARER_TOKEN`, `CUSTOM`, `BASIC`, `OAUTH_M2M`, `TRELLO_OAUTH1`, `GOOGLE_DWD`, `TRUSTED_IDP`, `SMART_FHIR`, `NO_AUTH`. | | `data.connection_name` | string | The connection's name, as shown in AgentKit > Connections. Omitted when it can't be resolved. | | `data.last_used_at` | string | When the connected account was last used. | | `data.token_expires_at` | string | When the access token expires, if known. | | `display_name` | string | Human-readable name for the event type. | | `environment_id` | string | The environment ID where the event occurred. | | `id` | string | Unique identifier for the webhook event (must be prefixed with "evt_"). | | `object` | string (enum) | The type of object that triggered the webhook. One of: `ConnectedAccount`. | | `occurred_at` | string | When the event occurred (ISO 8601 format). | | `spec_version` | string | The webhook specification version. | | `type` | string (enum) | The event type. One of: `connected_account.oauth_tokens_fetched`. | | `organization_id` | string | The organization ID (if applicable). | **Example** ```json { "spec_version": "1", "id": "evt_101652975398683152", "type": "connected_account.oauth_tokens_fetched", "occurred_at": "2026-10-02T14:32:00.512Z", "environment_id": "env_88640229614813449", "object": "ConnectedAccount", "data": { "id": "ca_24834495392086178", "identifier": "user_123", "connection_id": "conn_70219645518265104", "connection_name": "gmail", "provider": "GMAIL", "authorization_type": "OAUTH", "status": "ACTIVE", "token_expires_at": "2026-10-02T15:30:00Z" } } ``` ## connected_account.oauth_succeeded Triggered when OAuth authentication succeeds Scalekit sends the event as a JSON `POST` to your webhook endpoint, signed in the `webhook-id`, `webhook-timestamp` and `webhook-signature` headers. Verify the signature before you act on it; see [Listen for account events](https://docs.scalekit.com/agentkit/account-events/). **Payload** | Name | Type | Description | | --- | --- | --- | | `data` | object | The connected account the event is about. | | `data.connection_id` | string | The connection's ID. | | `data.id` | string | The connected account's ID. | | `data.identifier` | string | Your app's ID for the user, the value passed when the account was created. | | `data.provider` | string | The app, such as `GMAIL`. | | `data.status` | string (enum) | Current connected account status. One of: `ACTIVE`, `EXPIRED`, `PENDING_AUTH`, `PENDING_VERIFICATION`, `DISCONNECTED`. | | `data.authorization_type` | string (enum) | Authorization type of the connected account. One of: `OAUTH`, `API_KEY`, `BASIC_AUTH`, `BEARER_TOKEN`, `CUSTOM`, `BASIC`, `OAUTH_M2M`, `TRELLO_OAUTH1`, `GOOGLE_DWD`, `TRUSTED_IDP`, `SMART_FHIR`, `NO_AUTH`. | | `data.connection_name` | string | The connection's name, as shown in AgentKit > Connections. Omitted when it can't be resolved. | | `data.last_used_at` | string | When the connected account was last used. | | `data.token_expires_at` | string | When the access token expires, if known. | | `display_name` | string | Human-readable name for the event type. | | `environment_id` | string | The environment ID where the event occurred. | | `id` | string | Unique identifier for the webhook event (must be prefixed with "evt_"). | | `object` | string (enum) | The type of object that triggered the webhook. One of: `ConnectedAccount`. | | `occurred_at` | string | When the event occurred (ISO 8601 format). | | `spec_version` | string | The webhook specification version. | | `type` | string (enum) | The event type. One of: `connected_account.oauth_succeeded`. | | `organization_id` | string | The organization ID (if applicable). | **Example** ```json { "spec_version": "1", "id": "evt_101652975398683153", "type": "connected_account.oauth_succeeded", "occurred_at": "2026-10-02T14:33:00.512Z", "environment_id": "env_88640229614813449", "object": "ConnectedAccount", "data": { "id": "ca_24834495392086178", "identifier": "user_123", "connection_id": "conn_70219645518265104", "connection_name": "gmail", "provider": "GMAIL", "authorization_type": "OAUTH", "status": "ACTIVE", "token_expires_at": "2026-10-02T15:30:00Z" } } ``` ## connected_account.token_refresh_succeeded Triggered when token refresh succeeds Scalekit sends the event as a JSON `POST` to your webhook endpoint, signed in the `webhook-id`, `webhook-timestamp` and `webhook-signature` headers. Verify the signature before you act on it; see [Listen for account events](https://docs.scalekit.com/agentkit/account-events/). **Payload** | Name | Type | Description | | --- | --- | --- | | `data` | object | The connected account the event is about. | | `data.connection_id` | string | The connection's ID. | | `data.id` | string | The connected account's ID. | | `data.identifier` | string | Your app's ID for the user, the value passed when the account was created. | | `data.provider` | string | The app, such as `GMAIL`. | | `data.status` | string (enum) | Current connected account status. One of: `ACTIVE`, `EXPIRED`, `PENDING_AUTH`, `PENDING_VERIFICATION`, `DISCONNECTED`. | | `data.authorization_type` | string (enum) | Authorization type of the connected account. One of: `OAUTH`, `API_KEY`, `BASIC_AUTH`, `BEARER_TOKEN`, `CUSTOM`, `BASIC`, `OAUTH_M2M`, `TRELLO_OAUTH1`, `GOOGLE_DWD`, `TRUSTED_IDP`, `SMART_FHIR`, `NO_AUTH`. | | `data.connection_name` | string | The connection's name, as shown in AgentKit > Connections. Omitted when it can't be resolved. | | `data.last_used_at` | string | When the connected account was last used. | | `data.token_expires_at` | string | When the access token expires, if known. | | `environment_id` | string | The environment ID where the event occurred. | | `id` | string | Unique identifier for the webhook event (must be prefixed with "evt_"). | | `object` | string (enum) | The type of object that triggered the webhook. One of: `ConnectedAccount`. | | `occurred_at` | string | When the event occurred (ISO 8601 format). | | `spec_version` | string | The webhook specification version. | | `type` | string (enum) | The event type. One of: `connected_account.token_refresh_succeeded`. | | `organization_id` | string | The organization ID (if applicable). | **Example** ```json { "spec_version": "1", "id": "evt_101652975398683155", "type": "connected_account.token_refresh_succeeded", "occurred_at": "2026-10-02T14:35:00.512Z", "environment_id": "env_88640229614813449", "object": "ConnectedAccount", "data": { "id": "ca_24834495392086178", "identifier": "user_123", "connection_id": "conn_70219645518265104", "connection_name": "gmail", "provider": "GMAIL", "authorization_type": "OAUTH", "status": "ACTIVE", "token_expires_at": "2026-10-02T15:30:00Z" } } ``` ## connected_account.token_refresh_failed Triggered when token refresh fails Scalekit sends the event as a JSON `POST` to your webhook endpoint, signed in the `webhook-id`, `webhook-timestamp` and `webhook-signature` headers. Verify the signature before you act on it; see [Listen for account events](https://docs.scalekit.com/agentkit/account-events/). **Payload** | Name | Type | Description | | --- | --- | --- | | `data` | object | The connected account the event is about. | | `data.connection_id` | string | The connection's ID. | | `data.id` | string | The connected account's ID. | | `data.identifier` | string | Your app's ID for the user, the value passed when the account was created. | | `data.provider` | string | The app, such as `GMAIL`. | | `data.status` | string (enum) | Current connected account status. One of: `ACTIVE`, `EXPIRED`, `PENDING_AUTH`, `PENDING_VERIFICATION`, `DISCONNECTED`. | | `data.authorization_type` | string (enum) | Authorization type of the connected account. One of: `OAUTH`, `API_KEY`, `BASIC_AUTH`, `BEARER_TOKEN`, `CUSTOM`, `BASIC`, `OAUTH_M2M`, `TRELLO_OAUTH1`, `GOOGLE_DWD`, `TRUSTED_IDP`, `SMART_FHIR`, `NO_AUTH`. | | `data.connection_name` | string | The connection's name, as shown in AgentKit > Connections. Omitted when it can't be resolved. | | `data.last_used_at` | string | When the connected account was last used. | | `data.token_expires_at` | string | When the access token expires, if known. | | `environment_id` | string | The environment ID where the event occurred. | | `id` | string | Unique identifier for the webhook event (must be prefixed with "evt_"). | | `object` | string (enum) | The type of object that triggered the webhook. One of: `ConnectedAccount`. | | `occurred_at` | string | When the event occurred (ISO 8601 format). | | `spec_version` | string | The webhook specification version. | | `type` | string (enum) | The event type. One of: `connected_account.token_refresh_failed`. | | `organization_id` | string | The organization ID (if applicable). | **Example** ```json { "spec_version": "1", "id": "evt_101652975398683156", "type": "connected_account.token_refresh_failed", "occurred_at": "2026-10-02T14:36:00.512Z", "environment_id": "env_88640229614813449", "object": "ConnectedAccount", "data": { "id": "ca_24834495392086178", "identifier": "user_123", "connection_id": "conn_70219645518265104", "connection_name": "gmail", "provider": "GMAIL", "authorization_type": "OAUTH", "status": "EXPIRED" } } ``` ## connected_account.status_updated Triggered when a connected account's status changes between two states (for example PENDING_AUTH to ACTIVE, ACTIVE to EXPIRED, or any state to DISCONNECTED). Not triggered on account creation. Scalekit sends the event as a JSON `POST` to your webhook endpoint, signed in the `webhook-id`, `webhook-timestamp` and `webhook-signature` headers. Verify the signature before you act on it; see [Listen for account events](https://docs.scalekit.com/agentkit/account-events/). **Payload** | Name | Type | Description | | --- | --- | --- | | `data` | object | The connected account whose status changed, including its previous status. | | `data.authorization_type` | string (enum) | The authorization type of the connected account. One of: `OAUTH`, `API_KEY`, `BASIC_AUTH`, `BEARER_TOKEN`, `CUSTOM`, `BASIC`, `OAUTH_M2M`, `TRELLO_OAUTH1`, `GOOGLE_DWD`, `TRUSTED_IDP`, `SMART_FHIR`, `NO_AUTH`. | | `data.connection_id` | string | The connection's ID. | | `data.id` | string | The connected account's ID. | | `data.identifier` | string | Your app's ID for the user, the value passed when the account was created. | | `data.old_status` | string (enum) | The previous status of the connected account. One of: `ACTIVE`, `EXPIRED`, `PENDING_AUTH`, `PENDING_VERIFICATION`, `DISCONNECTED`. | | `data.provider` | string | The app, such as `GMAIL`. | | `data.status` | string (enum) | The new status of the connected account. One of: `ACTIVE`, `EXPIRED`, `PENDING_AUTH`, `PENDING_VERIFICATION`, `DISCONNECTED`. | | `data.connection_name` | string | The connection's name, as shown in AgentKit > Connections. Omitted when it cannot be resolved. | | `environment_id` | string | The environment ID where the event occurred. | | `id` | string | Unique identifier for the webhook event (must be prefixed with "evt_"). | | `object` | string (enum) | The type of object that triggered the webhook. One of: `ConnectedAccount`. | | `occurred_at` | string | When the event occurred (ISO 8601 format). | | `spec_version` | string | The webhook specification version. | | `type` | string (enum) | The event type. One of: `connected_account.status_updated`. | **Example** ```json { "spec_version": "1", "id": "evt_101652975398683149", "type": "connected_account.status_updated", "occurred_at": "2026-10-02T14:32:00.512Z", "environment_id": "env_88640229614813449", "object": "ConnectedAccount", "data": { "id": "ca_24834495392086178", "identifier": "user_123", "connection_id": "conn_70219645518265104", "connection_name": "gmail", "provider": "GMAIL", "authorization_type": "OAUTH", "status": "ACTIVE", "old_status": "PENDING_AUTH" } } ```