Handle CAPTCHA task results safely in TypeScript
Switch on result.status before reading result.solution. Only ready contains a usable solution, and even then you must check the fields your requested product needs: Kasada returns headers, while reCAPTCHA and TMPT return tokens with different uses.
On this page
Narrow the status before reading the solution
Use the SDK’s exported TaskRequest for input and TaskResult for responses. Narrowing the status lets TypeScript distinguish unfinished work, failure, and readiness without any or a cast that merely tells the compiler to trust you.
import { Solvethree, type TaskRequest, type TaskResult } from '@solvethree/sdk';
const client = new Solvethree({ apiKey: process.env.SOLVETHREE_API_KEY! });
const input: TaskRequest = { site: 'accounts.example.com', action: 'login', proxy: process.env.SOLVETHREE_PROXY! };
function inspect(result: TaskResult): void {
switch (result.status) {
case 'pending':
case 'solving':
console.log('Retrieve this task again:', result.id);
return;
case 'failed':
console.error('Failed task:', result.id, result.errorCode);
return;
case 'ready':
// Solution fields depend on the requested product; check optional fields.
if (!result.solution.token || !result.solution.userAgent || !result.solution.secChUa) {
throw new Error('Expected a complete reCAPTCHA solution');
}
console.log('Ready task:', result.id);
// Submit token privately with its browser identity and original proxy.
return;
}
}
inspect(await client.createTask(input, { waitMs: 0 }));
// Save the returned ID and call getTask while pending/solving with a deadline.The example creates one task and inspects the immediate response. pending and solving mean retrieve it again; failed stops processing; ready reaches the solution checks. If you extract the ready branch into a helper, use the exported ReadyTask type for its parameter. That narrows the status, but it doesn't make every product-specific solution field mandatory.
The guards check for a token and both browser-identity fields because this input requests reCAPTCHA. They don't prove that a token will be accepted by the target. The example deliberately doesn't print the token.
- Use Node.js 22+ and the official SDK tarball installation documented in the API docs; don't assume an npm registry release exists.
- Set
SOLVETHREE_API_KEYandSOLVETHREE_PROXYin the server environment. The!assertions don't validate missing environment variables at runtime. - Replace
accounts.example.comandloginwith the intended hostname and action. The site must already have an active workspace profile; see site profile setup.
Poll the saved ID, not a new task
The example leaves polling to the caller. Keep creation outside that loop: calling createTask again isn't how you check whether the first task has finished.
- 1.
Save the creation result’s ID
Check that the returned ID is a nonempty string before using it for retrieval. Keep it associated with the original request, which you'll also need when selecting the successful proxy.
- 2.
Retrieve that same task
Call
getTaskwith the saved ID while the status ispendingorsolving, with a local deadline. Before processing each response, check that its ID matches the saved ID. A matching ID establishes which task you're handling, not whether it succeeded. - 3.
Stop at a terminal result
On
ready, validate the fields required by the requested product. Onfailed, inspect botherrorCodeanderror, then stop polling that task. Handle request errors separately: an unsuccessful retrieval request isn't itself a terminal task failure.
If creation times out before you receive an ID, don't assume no task was created. If you supplied a requestId, resubmitting the identical request with that same value recovers the existing task. Reusing it with changed inputs conflicts; generating a new value doesn't recover the original task.
A ready result still needs the right product handler
Choose the handler from the original request, then check the expected solution fields. A token’s presence alone can't distinguish a reCAPTCHA response from a TMPT cookie value.
| Requested product | Solution value | How to use it |
|---|---|---|
| reCAPTCHA Enterprise | solution.token | Submit as the reCAPTCHA token for the intended hostname and action. |
| KasadaTask | solution.headers | Apply the returned headers together. Don't treat this result as a token. |
| TicketmasterTmptTask | solution.token | Use as the raw tmpt cookie value, not as a reCAPTCHA token. |
Standalone Kasada currently supports HTTPS targets on www.ticketmaster.com, not arbitrary sites. Its headers handler isn't a universal Kasada integration.
For each product, submit with the returned userAgent and secChUa and the successful proxy. With proxy candidates, use successfulProxy(originalTask, readyResult) rather than assuming the first candidate won. The SDK doesn't apply the solution to the target for you, and ready doesn't guarantee acceptance of that subsequent request.
Sources and API reference
Run your first task.
Start with $5 in workspace credit.