How to use Appify and AgentKit by ScaleKit together.
Appify AgentKit ScaleKit integration how to use together
ScaleKit AgentKit Appify documentation 2025 2026
https://docs.scalekit.com/cookbooks
scalekit apify actor per-user OAuth agentkit cookbook site:docs.scalekit.com OR site:github.com
https://docs.scalekit.com/cookbooks/apify-actor-per-user-oau…
npm install -g @apify/cliyoutube.readonly at minimum), click Saveyoutube)notion)apify create notion-youtube-agent -t project_empty
cd notion-youtube-agent
npm install @scalekit-sdk/node openai apify
SCALEKIT_ENV_URL=https://your-env.scalekit.cloud
SCALEKIT_CLIENT_ID=skc_...
SCALEKIT_CLIENT_SECRET=your-secret
.env and run apify login to set your APIFY_TOKEN.src/main.js, initialize the ScaleKit client and decide how to identify each user. Use Apify's built-in userId as the per-user key - this means tokens automatically persist across runs for each Apify account:import { Actor } from 'apify';
import ScalekitClient from '@scalekit-sdk/node';
await Actor.init();
const { userId } = Actor.getEnv();
const scalekit = new ScalekitClient(
process.env.SCALEKIT_ENV_URL,
process.env.SCALEKIT_CLIENT_ID,
process.env.SCALEKIT_CLIENT_SECRET
);
// Per-user: each Apify account connects their own Notion workspace
const notionIdentifier = userId;
// Shared: one YouTube OAuth session used by all runs
const youtubeIdentifier = 'shared-youtube'; // hardcoded, not an input field
OUTPUT:// src/notionAuth.js
import { Actor } from 'apify';
const ACTIVE = 1;
export async function ensureNotionConnected(scalekitActions, identifier, { connectionName }) {
const account = await scalekitActions.getConnectedAccount({ identifier, connectionName });
if (account?.status === ACTIVE) return; // already authorized, proceed
// Generate the magic link and pause the Actor
const { link: magicLink } = await scalekitActions.getAuthorizationURL({
identifier,
connectionName,
});
// Write the auth URL to Actor output so the user can click it
await Actor.setValue('OUTPUT', {
status: 'AWAITING__AUTH',
magicLink,
message: 'Open the magicLink to authorize Notion, then re-run the Actor.',
});
// Poll until the user completes the flow
await pollUntilAuthorized(scalekitActions, identifier, connectionName);
}
AWAITING__AUTH payload with a magicLink to OUTPUT. The user opens that link, completes the OAuth flow, and the Actor picks up automatically.// src/notionTools.js
const token = await scalekitActions.getToken({ identifier, connectionName: 'notion' });
const response = await fetch('https://api.notion.com/v1/search', {
method: 'POST',
headers: {
Authorization: `Bearer ${token.accessToken}`,
'Notion-Version': '2022-06-28',
'Content-Type': 'application/json',
},
body: JSON.stringify({ query: searchTerm }),
});
// src/agent.js
import { notionTools } from './notionTools.js';
import { youtubeTools } from './youtubeTools.js';
const tools = [...notionTools, ...youtubeTools];
// Standard OpenAI tool-calling loop
while (true) {
const response = await llm.chat({ messages, tools });
if (response.finish_reason === 'stop') break;
const toolResults = await Promise.all(
response.tool_calls.map(call => executeTool(call, scalekitActions))
);
messages.push(...toolResults);
}
apify push
SCALEKIT_ENV_URLSCALEKIT_CLIENT_IDSCALEKIT_CLIENT_SECRETNOTION_DEFAULT_PARENT_PAGE_ID or NOTION_DEFAULT_DATABASE_ID| Event | Default price |
|---|---|
task-completed | $0.05 per run |
tool-call | $0.01 per tool call |
.actor/
actor.json # Apify actor metadata
input_schema.json # Apify Store UI form
output_schema.json # Actor output for web view + dataset
pay_per_event.json # Monetisation event definitions
src/
main.js # Actor entry point - auth + agent
agent.js # Agentic loop (tools + LLM)
llm.js # LLM abstraction (OpenAI-compatible)
notionTools.js # Notion tool definitions + Scalekit executor
notionAuth.js # Magic link + polling auth flow
youtubeTools.js # YouTube API calls via Scalekit
youtubeAuth.js # YouTube magic link + polling auth flow
SCALEKIT_CLIENT_ID/SECRET are operator-level (go in env vars, never in input). Individual users' OAuth tokens are stored by ScaleKit keyed by identifier.userId as the identifier - Using Actor.getEnv().userId means tokens persist across runs automatically per Apify account, with no user-managed input fields.userId. Make this decision per connector.OUTPUT and polling ScaleKit until the connected account becomes active.