wait_for_sms_code
Purchase a virtual phone number and automatically wait for SMS verification codes from services like Telegram or WhatsApp. Uses WebSocket delivery with polling fallback and provides recovery options for timeout scenarios.
Instructions
RECOMMENDED: One-step tool that buys a number AND waits for the SMS code automatically. Uses real-time WebSocket delivery with automatic polling fallback. Always returns order_id in the response — even on timeout — so you can use check_sms to recover.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| service | Yes | Service code (e.g. "telegram", "whatsapp", "google") | |
| country | Yes | Country ISO code (e.g. "US", "GB", "RU") | |
| timeout_seconds | No | How long to wait for SMS code in seconds (default: 120, max: 600) |
Implementation Reference
- src/tools.ts:639-759 (handler)The handler for `wait_for_sms_code`. It handles buying the number and then waiting for an SMS via WebSocket, with a fallback to polling.
export async function handleWaitForCode( client: VirtualSMSClient, args: z.infer<typeof WaitForCodeInput> ) { const timeoutMs = (args.timeout_seconds ?? 120) * 1000; const pollIntervalMs = 5000; const startTime = Date.now(); // Step 1: Buy the number let order; try { order = await client.createOrder(args.service, args.country); } catch (err) { throw new Error(`Failed to buy number: ${(err as Error).message}`); } const orderId = order.order_id; const phoneNumber = order.phone_number; const apiKey = client.getApiKey(); const baseUrl = client.getBaseUrl(); // Step 2: Try WebSocket first (if we have an API key) if (apiKey) { const remainingMs = timeoutMs - (Date.now() - startTime); const wsResult = await waitForSMSViaWebSocket(baseUrl, apiKey, orderId, remainingMs); if (wsResult) { return { content: [ { type: 'text' as const, text: JSON.stringify( { success: true, phone_number: phoneNumber, sms_code: wsResult.sms_code, sms_text: wsResult.sms_text, order_id: orderId, delivery_method: wsResult.delivery_method, elapsed_seconds: Math.round((Date.now() - startTime) / 1000), }, null, 2 ), }, ], }; } // WS timed out or failed — fall through to polling } // Step 3: Polling fallback let attempts = 0; while (Date.now() - startTime < timeoutMs) { attempts++; try { const status = await client.getOrder(orderId); if (status.sms_code) { return { content: [ { type: 'text' as const, text: JSON.stringify( { success: true, phone_number: phoneNumber, sms_code: status.sms_code, sms_text: status.sms_text, order_id: orderId, delivery_method: 'polling', elapsed_seconds: Math.round((Date.now() - startTime) / 1000), poll_attempts: attempts, }, null, 2 ), }, ], }; } if (status.status === 'cancelled' || status.status === 'failed') { throw new Error( `Order ${orderId} was ${status.status} before SMS arrived.` ); } } catch (err) { const message = (err as Error).message; if (!message.includes('waiting') && !message.includes('pending')) { throw err; } } const remaining = timeoutMs - (Date.now() - startTime); if (remaining <= 0) break; await sleep(Math.min(pollIntervalMs, remaining)); } // Timeout — return order_id for crash recovery (don't cancel automatically) return { content: [ { type: 'text' as const, text: JSON.stringify( { success: false, error: 'timeout', message: `No SMS received within ${args.timeout_seconds} seconds.`, order_id: orderId, phone_number: phoneNumber, tip: 'Use check_sms with this order_id to check if code arrived later, or cancel_order to get a refund.', }, null, 2 ), }, ], }; } - src/tools.ts:29-38 (schema)Input schema for the `wait_for_sms_code` tool.
export const WaitForCodeInput = z.object({ service: z.string().describe('Service code (e.g. "telegram", "whatsapp", "google")'), country: z.string().describe('Country ISO code (e.g. "US", "GB", "RU")'), timeout_seconds: z.number() .int() .min(10) .max(600) .default(120) .describe('How long to wait for SMS code in seconds (default: 120, max: 600)'), }); - src/tools.ts:236-268 (registration)Registration of `wait_for_sms_code` in the `TOOL_DEFINITIONS` list.
name: 'wait_for_sms_code', title: 'Buy Number and Wait for SMS Code', description: 'RECOMMENDED: One-step tool that buys a number AND waits for the SMS code automatically. ' + 'Uses real-time WebSocket delivery with automatic polling fallback. ' + 'Always returns order_id in the response — even on timeout — so you can use check_sms to recover.', inputSchema: { type: 'object' as const, properties: { service: { type: 'string', description: 'Service code (e.g. "telegram", "whatsapp", "google")', }, country: { type: 'string', description: 'Country ISO code (e.g. "US", "GB", "RU")', }, timeout_seconds: { type: 'number', description: 'How long to wait for SMS code in seconds (default: 120, max: 600)', default: 120, }, }, required: ['service', 'country'], }, annotations: { title: 'Buy Number and Wait for SMS Code', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true, }, }, - src/tools.ts:553-637 (helper)Helper function for `wait_for_sms_code` to handle WebSocket communication.
function waitForSMSViaWebSocket( baseUrl: string, apiKey: string, orderId: string, timeoutMs: number ): Promise<SMSResult | null> { return new Promise((resolve) => { const wsUrl = baseUrl.replace(/^http/, 'ws') + `/ws/orders?order_id=${encodeURIComponent(orderId)}&api_key=${encodeURIComponent(apiKey)}`; let ws: WebSocket | null = null; let resolved = false; let reconnected = false; const timer = setTimeout(() => { if (!resolved) { resolved = true; ws?.close(); resolve(null); // trigger polling fallback } }, timeoutMs); function connect() { ws = new WebSocket(wsUrl); ws.on('error', () => { if (!resolved && !reconnected) { reconnected = true; ws?.close(); // Try once more after 1s setTimeout(connect, 1000); } else if (!resolved) { resolved = true; clearTimeout(timer); resolve(null); // WS failed, use polling } }); ws.on('close', () => { if (!resolved && !reconnected) { reconnected = true; setTimeout(connect, 1000); } else if (!resolved) { resolved = true; clearTimeout(timer); resolve(null); // WS closed, use polling } }); ws.on('message', (data: Buffer) => { try { const msg = JSON.parse(data.toString()); // Message format from server: {type: "sms", code: "...", full_text: "..."} if (msg.type === 'sms' && msg.code) { if (!resolved) { resolved = true; clearTimeout(timer); ws?.close(); resolve({ sms_code: msg.code, sms_text: msg.full_text, delivery_method: 'websocket', }); } } // Also handle sms_received type from backend if (msg.type === 'sms_received' && msg.code) { if (!resolved) { resolved = true; clearTimeout(timer); ws?.close(); resolve({ sms_code: msg.code, sms_text: msg.message, delivery_method: 'websocket', }); } } } catch { // ignore parse errors } }); } connect(); }); }