Skip to main content
Credentials are consumed by actions. Agents and workflows use credentials indirectly when they call those actions as tools or steps. This page covers the runtime model: how to declare credentials, how Keystroke resolves a credential instance, and how to pin scopes when a run should use a project, organization, or user connection.

Declare credentials on an action

Use defineCredential() to describe the credential an action needs, then list it in the action’s credentials array.
src/actions/search.ts
import { defineAction } from "@keystrokehq/keystroke/action";
import { defineCredential } from "@keystrokehq/keystroke/credentials";
import { z } from "zod";

const exa = defineCredential({
  key: "exa",
  fields: { apiKey: z.string() },
});

export const search = defineAction({
  slug: "search",
  input: z.object({ query: z.string() }),
  output: z.object({ results: z.array(z.string()) }),
  credentials: [exa] as const,
  async run(input, credentials) {
    return callSearchApi(input.query, credentials.exa.apiKey);
  },
});
The credential key (exa) is the app/credential slug Keystroke looks up in the vault. The schema validates the resolved secret before your action runs.
Credentials belong on actions, not on defineAgent() or defineWorkflow(). Agents and workflows get credentials by running actions that declare them.

Credential kinds

There are three runtime credential kinds:
KindAuthoring shapeUse it for
api_keydefineCredential({ key, fields }) or credential.static()Static API keys and secret fields
oauth_manageddefineCredential({ key, kind: "oauth" }) or credential.oauth()Native OAuth connections, such as Slack gateway credentials
keystrokedefineCredential({ key, kind: "keystroke" }) or generated app credentialsHosted catalog apps routed through Keystroke’s platform MCP layer
Most custom credentials are static API keys. Built-in app packages usually define the credential for you.

Use actions in workflows and agents

Once an action declares credentials, use it like any other action.
// Workflow step
const results = await search.run({ query: "agentic workflows" });

// Agent tool
export default defineAgent({
  slug: "researcher",
  systemPrompt: "Use the search tool for current web research.",
  model: "anthropic/claude-sonnet-4.6",
  tools: [search],
});
The runner resolves credentials immediately before the action executes. The model does not see the secret.

Resolution order

When an action runs, Keystroke resolves each credential requirement using the run context. Resolution order:
  1. Explicit selection: if the platform supplied a credential instance id for this run, that exact instance wins.
  2. Pinned scope: if the action or credential is scoped with .scope(...), only that scope is tried.
  3. Project default: for unpinned credentials, try the default project credential.
  4. Organization default: if no project credential resolves, try the default organization credential.
  5. Error: if nothing resolves, the run fails with a missing-credentials error.
User credentials are intentionally not part of the unpinned chain. If you want a user connection, pin user scope.

Pin a scope

Use .scope() when the action must use a specific credential scope.
// As an agent tool: run with the user's connected account
tools: [slackSendMessage.scope("user")],

// As a workflow step: use the org credential
await slackSendMessage.run({ channel, markdown_text }).scope("organization");
You can pin a credential definition or an action. Pinning an action applies the scope to all credentials that action declares.
ScopeWhen to pin
organizationA shared org credential should always be used
projectThe workflow should use a project-specific secret
userThe action should run as the user who connected the app
If a pinned scope has no matching credential in the run context, Keystroke does not fall back to another scope.

Defaults and multiple credentials

Within a scope, Keystroke can resolve a credential automatically when:
  • There is exactly one credential instance for that app and scope.
  • Or there are multiple instances, but exactly one is marked as default.
If there are multiple possible credentials and no single default, the run needs an explicit selection. This is why labels and defaults matter when you manage credentials from the Apps page or CLI.
keystroke credentials update <credential-id> --label "Production Slack" --default
To make one specific step, agent tool, or poll action use a particular instance regardless of the default, bind it with an assignment.

Troubleshooting

When a credential can’t be resolved, the action fails before run executes, with a message that points at the fix:
MessageCauseFix
”This agent needs the <key> credential…”Nothing resolved for that key at any tried scopeConnect the credential (keystroke connect <key> or the Apps page), or check the action’s pinned scope
”Multiple <key> credentials are available. Pick one…”More than one instance and no defaultMark one default, or pass an explicit selection
User scope has an extra prerequisite: the run must know which user it is acting as. A .scope("user") credential only resolves when the run carries a user actor, such as an agent prompt tied to a signed-in user. Background runs with no user (most cron and webhook triggers) cannot resolve user-scoped credentials. The Slack gateway runs as the workspace’s connected account, not the individual who sent the message, so scope its actions to organization or project rather than user.

Cloud and local behavior

Cloud execution and local runs use the same resolution rules, but different stores.
TargetWhere credentials come from
CloudThe hosted platform credential store, materialized to workers on demand
Local (keystroke dev, --local)The local server credential store
Deploying a project does not upload .env. If a deployed workflow uses an action with credentials, connect those credentials in the cloud first.

Next steps

Connect and manage apps

Create credential instances and choose their scopes.

Actions

Learn how actions define typed inputs, outputs, and credentials.

Workflow steps

Run credentialed actions as durable workflow steps.

Agent tools

Attach credentialed actions as tools an agent can call.