Docs
Dashboard
Examples / LoginsBeta

Hand over for a code

The AI signs in with your username and a password kept as a credential; when the site asks for a code sent by SMS or an app, it pauses and asks you, then types the code and carries on. The run names the credential (credentials: ["SIGNIN_PASSWORD"]) and the model only ever sees %SIGNIN_PASSWORD%.

When to use it: The AI signs in for you, but the site sends a one-time code to your phone.

Usescredentials.create (the password as a secret, limited to the sign-in site with origins), agent.run with credentials and variables, agent.stream, agent.resume with a note
Needsa plan with room for credentials, and a model on the API
Siteyour sign-in page (SIGNIN_URL). The runner uses its stand-in (password, then a 6-digit code) and types the code
Outputoutput/result.json
LanguagesNode and Python

You take part: When the AI asks, type the code the site sent you.

The code

The example as it is in its folder. Both versions do the same job and write the same output/, so one result check covers both.

/** * Hand over for a code: the AI signs in with your username and a password kept as a credential, and when the * site asks for a code sent by SMS (or an app), it pauses and asks you for it, then carries on. * *   SIGNIN_URL=https://… SITE_USERNAME=… SITE_PASSWORD=… npx tsx node/index.ts     (BOXLINE_API_KEY) * * The password goes into the credential SIGNIN_PASSWORD, a secret (SECRET_NAME picks another name), limited to the * sign-in site; after that SITE_PASSWORD is not needed (set the credential in the console instead, if you like). The run * names the credential, and the model only ever sees %username% and %SIGNIN_PASSWORD%. Writes output/result.json. */import { mkdirSync, writeFileSync } from "node:fs";import { join } from "node:path";import { createInterface } from "node:readline/promises";import { Boxline, CredentialExistsError } from "@boxline/sdk";const out = process.env.OUTPUT_DIR ?? "output";const signin = process.env.SIGNIN_URL;const username = process.env.SITE_USERNAME;const secretName = process.env.SECRET_NAME ?? "SIGNIN_PASSWORD";if (!signin || !username) throw new Error("Set SIGNIN_URL and SITE_USERNAME (and SITE_PASSWORD the first time)");const bx = new Boxline();// The password as a credential (a secret): stored sealed, never shown again, and typed only into fields on the sign-in site.let secretCreated = false;if (process.env.SITE_PASSWORD) {  const origins = [new URL(signin).origin];  try {    await bx.credentials.create({ name: secretName, type: "secret", value: process.env.SITE_PASSWORD, origins, description: `Password for ${new URL(signin).host}` });    secretCreated = true;  } catch (err) {    if (!(err instanceof CredentialExistsError)) throw err;    await bx.credentials.update(secretName, { value: process.env.SITE_PASSWORD, origins });  }}console.log(`Credential ${secretName}: ${JSON.stringify(await bx.credentials.get(secretName).then(({ origins, scope }) => ({ origins, scope })))}`);const run = await bx.agent.run({  task:    `Open ${signin} and sign in with the username %username% and the password %${secretName}%. When the site asks for a ` +    "verification code, ask me for it with ask_user_for_help and wait; then type the code I give you. When I'm signed in, " +    "tell me the name shown on the page.",  variables: { username },  credentials: [secretName], // the model sees %SIGNIN_PASSWORD%; the platform types the value  maxSteps: 25,});console.log(`Agent run ${run.id} (${run.provider}/${run.model}) · Session: ${run.sessionId}`);const rl = createInterface({ input: process.stdin, output: process.stdout });let handovers = 0;for await (const e of bx.agent.stream(run.id)) {  if (e.type === "tool") console.log(`→ ${e.name} ${JSON.stringify(e.input ?? {}).slice(0, 100)}`); // placeholders, never values  else if (e.type === "handover" && e.by === "agent") {    handovers++;    console.log(`\nThe AI asks: ${e.text}`);    const code = await rl.question("Type the code the site sent you: ");    await bx.agent.resume(run.id, { note: `The verification code is ${code.trim()}` });  } else if (e.type === "done") console.log(`\n${e.status}: ${e.result ?? e.error}`);}rl.close();const done = await bx.agent.get(run.id);mkdirSync(out, { recursive: true });writeFileSync(  join(out, "result.json"),  JSON.stringify(    { signinUrl: signin, secret: secretName, agentRuns: [{ id: run.id }], status: done.status, answer: done.result, handovers, steps: done.steps.length, model: done.model, made: { credentials: secretCreated ? [secretName] : [] } },    null,    2,  ),);

Inputs

Environment variables the example reads:

  • SIGNIN_URL: your sign-in page (required).
  • SITE_USERNAME: your username (required).
  • SITE_PASSWORD: your password, stored into the credential the first time (keep it in the environment, never in code; later runs need only the credential).
  • SECRET_NAME: the credential's name (default SIGNIN_PASSWORD).

Run it

Get the examples with git clone https://github.com/Boxline-dev/examples.git and open this one: cd examples/hand-over-for-a-code. Put your API key in BOXLINE_API_KEY (and BOXLINE_API_URL for another API), then run either version from the example’s folder. Both write to output/ and stop their sessions when they finish.

# Node 18 or newer, in the example's folderexport BOXLINE_API_KEY=…   # your API keynpm install @boxline/sdk tsxnpx tsx node/index.ts

Then check the result, from the same folder: npx tsx check.ts output. It exits with 0 only when the result is right.

What its check proves

check.ts reads what the example wrote and passes only when the result is right, not merely when the example ran.

The site saw the password and then the code, the AI asked at least once and named the signed-in user, the run the API keeps never holds the password, and the credential's use by the run is in its audit log.

Source

github.com/Boxline-dev/examples/hand-over-for-a-code on GitHub, with its README.md, check.ts and .env.example. The examples are open source. See all examples.

These docs describe a beta API. Endpoints, fields and SDK methods may change before general availability; breaking changes will be listed in the changelog.