Documentation Index
Fetch the complete documentation index at: https://tbd-6fc993ce-mintlify-profile-hot-load-save-changes-21459.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Build your own credential collection UI instead of using the hosted page. Poll for login fields, then submit credentials via the API.
Use the Programmatic flow when:
- You need a custom credential collection UI that matches your app’s design
- You’re building headless/automated authentication
- You have credentials stored and want to authenticate without user interaction
How It Works
Create Auth Agent and Start Authentication
Poll and Submit
Poll until step becomes awaiting_input, then submit credentials
Handle 2FA
If more fields appear (2FA code), submit again—same loop handles it
Getting started
1. Create an Auth Agent
const agent = await kernel.agents.auth.create({
domain: 'github.com',
profile_name: 'github-profile',
});
2. Start Authentication
const invocation = await kernel.agents.auth.invocations.create({
auth_agent_id: agent.id,
});
To save credentials for automatic re-authentication:
const invocation = await kernel.agents.auth.invocations.create({
auth_agent_id: agent.id,
save_credential_as: 'my-saved-creds',
});
3. Poll and Submit Credentials
A single loop handles everything—initial login, 2FA, and completion:
let state = await kernel.agents.auth.invocations.retrieve(invocation.invocation_id);
while (state.status === 'IN_PROGRESS') {
// Submit when fields are ready (login or 2FA)
if (state.step === 'awaiting_input' && state.pending_fields?.length) {
const fieldValues = getCredentialsForFields(state.pending_fields);
await kernel.agents.auth.invocations.submit(
invocation.invocation_id,
{ field_values: fieldValues }
);
}
await new Promise(r => setTimeout(r, 2000));
state = await kernel.agents.auth.invocations.retrieve(invocation.invocation_id);
}
if (state.status === 'SUCCESS') {
console.log('Authentication successful!');
}
The pending_fields array tells you what the login form needs:
// Example pending_fields for login
[{ name: 'username', type: 'text' }, { name: 'password', type: 'password' }]
// Example pending_fields for 2FA
[{ name: 'otp', type: 'code' }]
Complete Example
import Kernel from '@onkernel/sdk';
const kernel = new Kernel();
// Create auth agent
const agent = await kernel.agents.auth.create({
domain: 'github.com',
profile_name: 'github-profile',
});
const invocation = await kernel.agents.auth.invocations.create({
auth_agent_id: agent.id,
});
// Single polling loop handles login + 2FA
let state = await kernel.agents.auth.invocations.retrieve(invocation.invocation_id);
while (state.status === 'IN_PROGRESS') {
if (state.step === 'awaiting_input' && state.pending_fields?.length) {
// Check what fields are needed
const fieldNames = state.pending_fields.map(f => f.name);
if (fieldNames.includes('username')) {
// Initial login
await kernel.agents.auth.invocations.submit(
invocation.invocation_id,
{ field_values: { username: 'my-username', password: 'my-password' } }
);
} else {
// 2FA or additional fields
const code = await promptUserForCode();
await kernel.agents.auth.invocations.submit(
invocation.invocation_id,
{ field_values: { [state.pending_fields[0].name]: code } }
);
}
}
await new Promise(r => setTimeout(r, 2000));
state = await kernel.agents.auth.invocations.retrieve(invocation.invocation_id);
}
if (state.status === 'SUCCESS') {
console.log('Authentication successful!');
const browser = await kernel.browsers.create({
profile: { name: 'github-profile' },
stealth: true,
});
// Navigate to the site—you're already logged in
await page.goto('https://github.com');
}
The basic polling loop handles pending_fields, but login pages can require other input types too.
When the login page has “Sign in with Google/GitHub/Microsoft” buttons, they appear in pending_sso_buttons:
if (state.pending_sso_buttons?.length) {
// Show the user available SSO options
for (const btn of state.pending_sso_buttons) {
console.log(`${btn.provider}: ${btn.label}`);
}
// Submit the selected SSO button
await kernel.agents.auth.invocations.submit(
invocation.invocation_id,
{ sso_button: state.pending_sso_buttons[0].selector }
);
}
Remember to set allowed_domains on the agent to include the OAuth provider’s domain (e.g., accounts.google.com).
MFA Selection
When the site offers multiple MFA methods, they appear in mfa_options:
if (state.mfa_options?.length) {
// Available types: sms, email, totp, push, call, security_key
for (const opt of state.mfa_options) {
console.log(`${opt.type}: ${opt.label}`);
}
// Submit the selected MFA method
await kernel.agents.auth.invocations.submit(
invocation.invocation_id,
{ selected_mfa_type: 'sms' }
);
}
After selecting an MFA method, the flow continues—poll for pending_fields to submit the code, or handle external actions for push/security key.
External Actions (Push, Security Key)
When the site requires an action outside the browser (push notification, security key tap), the step becomes awaiting_external_action:
if (state.step === 'awaiting_external_action') {
// Show the message to the user
console.log(state.external_action_message);
// e.g., "Check your phone for a push notification"
// Keep polling—the flow resumes automatically when the user completes the action
}
Step Reference
The step field indicates what the flow is waiting for:
| Step | Description |
|---|
discovering | Finding the login page and analyzing it |
awaiting_input | Waiting for field values, SSO button click, or MFA selection |
submitting | Processing submitted values |
awaiting_external_action | Waiting for push approval, security key, etc. |
Status Reference
The status field indicates the overall invocation state:
| Status | Description |
|---|
IN_PROGRESS | Authentication is ongoing—keep polling |
SUCCESS | Login completed, profile saved |
FAILED | Login failed (check error_message) |
EXPIRED | Invocation timed out (5 minutes) |
CANCELED | Invocation was canceled |