start_2fa_for_action
Start a 2FA challenge to authorize a sensitive action after an API call returns 403 2fa_required, using a channel different from the user's last login.
Instructions
Start a 2FA challenge that grants the authenticated user permission to perform a sensitive action. The 2FA must complete on a channel DIFFERENT from the user's last login channel (the backend enforces this). On success a grant is stored keyed by the user id and action_type; middleware on the protected endpoint reads the grant and allows the mutation.
Destructive action types (api_key_create, api_key_revoke, api_key_regenerate, api_key_view, account_delete, phone_remove, email_remove, phone_primary, email_primary, revocation_bulk, domain_remove, profile_delete) are capped at 5min TTL and forced single-use by server policy. A step-up for revocation_bulk, phone_remove, email_remove, domain_remove, profile_delete or account_delete is bound to targets — the exact ids the protected call will carry. Configuration scopes (settings_write, profile_update, templates_write) allow up to 24h TTL and caller-chosen single_use — useful for agents making multiple settings mutations in one session.
Agent usage: (1) Attempt the sensitive call; if you get 403 2fa_required note the action_type. (2) Call start_2fa_for_action with that action_type and a channel different from the login channel. (3) Present the challenge: on telegram/whatsapp pass deep_link to render_auth_link; on sms show sms_message and the number to send it to; on email check the response first: with more than one verified email and no email_id given, it answers 200 with requires_email_selection: true and available_emails — NO session was started and nothing was sent, so ask the user which address and call again with email_id rather than telling them to open a mailbox. Never hand qr_text to a link renderer — it is the link already rendered as QR art. Print it verbatim inside a fenced code block only when a real terminal needs the QR. (4) Call wait_for_2fa until the session is verified. (5) Retry the original sensitive call.
The user does not see tool results — most clients fold them away, render_auth_link's output included. Before you wait or poll, put what the user must act on into your own reply text: the link itself (not a sentence announcing it), the SMS text and number, or a note to check their inbox.
ACCESS: needs a Proof account. Set PROOF_API_KEY and restart this server, then call this tool again. start_login does NOT open this tool.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| channel | Yes | Channel for the 2FA challenge — must differ from the user's last login channel | |
| targets | No | The ids the step-up is FOR — the grant is bound to exactly these and refused for any other request (`reason: target_mismatch`). revocation_bulk: the asset id you will pass to revoke_asset (one code per asset); phone_remove / email_remove: the phone or email record id; domain_remove: the domain id; profile_delete: the sub-account id; account_delete: your own user id (get_current_user). Leave out for the other action types. | |
| email_id | No | Optional: specific UserEmail id for email-channel 2FA | |
| phone_id | No | Optional: specific UserPhone id for phone-channel 2FA | |
| single_use | No | Whether the grant is consumed on first use. Server forces true for destructive action types. Defaults to true. | |
| action_type | Yes | The sensitive action the 2FA grant will authorize: any action type the API documents. The server caps each by its policy: destructive types (e.g. api_key_*, *_remove, revocation_bulk, account_delete) to 5 min TTL and single use; configuration scopes (e.g. settings_write, webhook_write) up to 24h with caller-chosen single_use. | |
| ttl_seconds | No | Requested grant lifetime in seconds. Server caps to 300s for destructive action types, 86400s for configuration scopes. Defaults to 300 if omitted. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| No | The address the code went to, masked (`email` only) | ||
| status | No | ||
| channel | No | ||
| message | No | ||
| qr_code | No | Base64-encoded PNG QR code for `deep_link` | |
| qr_text | No | UTF-8 text QR for `deep_link`, for terminal/agent rendering | |
| sms_code | No | The short code the person texts (`sms` only) | |
| sms_dids | No | Our SMS numbers, one entry per region and always all three. A region with no number configured carries an empty `did` and no QR code. | |
| deep_link | No | Opens Telegram or WhatsApp with what the person sends (`telegram`, `whatsapp`) | |
| email_sent | No | `true` when the code was emailed (`email` only) | |
| expires_at | No | ||
| session_id | No | ||
| sms_message | No | The message to send (`sms` only) | |
| instructions | No | One line telling the person what to do | |
| available_emails | No | The addresses the code may go to; call again with one of their ids as `email_id` | |
| requires_email_selection | No | Always `true`: no session was started, pick an address and start again | |
| second_method_recommended | No | Present (true) when the step-up uses the channel the account signed in with, because the account has no other; adding a second channel is recommended |