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 readrequired_action.submit_input(kind: otp / text / confirm,channel: totp | sms | email | unknown,prompt,submit.path). - MCP:
create_response(defaultwait: true) returnshuman_action_required: trueplus anmfaobject (channel, message, submit path) so you do not have to parse nested JSON. - At production scale, register outbound webhooks and handle
response.requires_actioninstead 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.
- Call
create_response(defaultwait: true). - If the result has
human_action_requiredorstatus: requires_action, ask the human in chat immediately. submit_inputwithcodeoranswer.get_responsewithwait: trueuntil 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
- Auth on target sites — vault login, custody
- Create a response → MFA — OpenAPI field reference
- MCP server
- Fallback semantics — why MFA does not advance the chain