MFA / OTP

Site-login MFA during a live run — not admin-dashboard TOTP. When a target site asks for MFA, 2FA, or an OTP, the run pauses immediately and asks the user for the code. It does not keep trying other runners.

The run pauses immediately

Outer status becomes requires_action (required_action.type = submit_input). banana_peel.status may show needs_human. That pause does not walk your fallback chain — the next runner would hit the same wall. Do not retry the task or pick another runner.

How you see the prompt

Poll GET /api/v1/responses/:id until status is requires_action (or handle the response.requires_action webhook). MCP create_response waits until that pause and returns human_action_required plus required_action.

  • HTTP: poll GET /api/v1/responses/{id} and read required_action.submit_input (kind: otp / text / confirm, channel: totp | sms | email | unknown, prompt, submit.path).
  • MCP: create_response (default wait: true) returns human_action_required: true plus an mfa object (channel, message, submit path) so you do not have to parse nested JSON.
  • At production scale, register outbound webhooks and handle response.requires_action instead of spinning GET — polling still works.

How you submit

Submit the code with POST /api/v1/responses/{id}/input { "code": "123456" } (or { "answer": "…" } for text/confirm). MCP: submit_input({ id, code }). Then keep polling until completed or failed.

export BANANA_PEEL_BASE=https://bananapeel.com
export BANANA_PEEL_API_KEY=bp_live_…

# 1. Create in the background so you get an id to poll
curl -s "$BANANA_PEEL_BASE/api/v1/responses" \
  -H "Authorization: Bearer $BANANA_PEEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "banana-peel",
    "input": "Log into https://example.com and download the latest invoice",
    "routing": "smart",
    "background": true,
    "credential_id": "cred_…"
  }'
# → { "id": "resp_…", "status": "in_progress", … }

# 2. Poll until completed | failed | requires_action
curl -s "$BANANA_PEEL_BASE/api/v1/responses/$ID" \
  -H "Authorization: Bearer $BANANA_PEEL_API_KEY"
# when paused:
# {
#   "status": "requires_action",
#   "required_action": {
#     "type": "submit_input",
#     "submit_input": {
#       "kind": "otp",
#       "channel": "sms",
#       "prompt": "Enter the SMS code…",
#       "message": "Enter the SMS code…",
#       "submit": { "method": "POST", "path": "/api/v1/responses/resp_…/input" }
#     }
#   }
# }

# 3. Submit the code — then keep polling. Do not start a new run.
curl -s -X POST "$BANANA_PEEL_BASE/api/v1/responses/$ID/input" \
  -H "Authorization: Bearer $BANANA_PEEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"code":"123456"}'

Cursor / MCP

ASK THE HUMAN IN CHAT NOW for the site MFA/OTP code. Call submit_input with their code. Do not retry the task or pick another runner.

  1. Call create_response (default wait: true).
  2. If the result has human_action_required or status: requires_action, ask the human in chat immediately.
  3. submit_input with code or answer.
  4. get_response with wait: true until terminal. Do not start a new run.
# create_response waits until completed | failed | requires_action
create_response({
  input: "Log into the site and extract the invoice total",
  url: "https://example.com/login",
  routing: "smart"
})
# → human_action_required: true
#   status: "requires_action"
#   mfa: { type: "mfa", channel: "sms", message: "…", submit: { method: "POST", path: "/api/v1/responses/resp_…/input" } }
#   required_action: { type: "submit_input", submit_input: { kind: "otp", prompt: "…" } }

# ASK THE HUMAN IN CHAT NOW, then:
submit_input({ id: "resp_…", code: "123456" })
# or: submit_input({ id: "resp_…", answer: "yes" })

# poll to terminal — do not retry the task or pick another runner
get_response({ id: "resp_…", wait: true })

CAPTCHA is not MFA

CAPTCHA and other bot-detection gates (reCAPTCHA, hCaptcha, Turnstile, DataDome, Cloudflare challenge, “prove you are human”) are solved, re-routed, or end blocked with reason bot_challenge. They never become a human text prompt.

Vault TOTP does not pause

If the vault credential (or request totp_secret) includes a base32 TOTP seed, OTP asks are answered in-process and the run does not pause. Username/password alone is not enough — the run still pauses for the code.

Store the seed on POST /api/v1/credentials as totp_secret, or pass totp_secret on the create. Username/password alone still pauses for the code.

See also

Command Palette

Search for a command to run...