Keep your CAPTCHA API key out of browser JavaScript
Keep the solvethree workspace API key on your server. The browser owns the form and application session; your authenticated backend owns task creation and retrieval. Don’t replace a leaked browser key with an unrestricted backend route that anyone can use to spend your workspace balance.
On this page
NEXT_PUBLIC is the wrong place for a workspace key
A workspace key authenticates requests to solvethree with Authorization: Bearer. Putting it in browser JavaScript, including through a bundled public environment variable such as NEXT_PUBLIC_SOLVETHREE_API_KEY, exposes that credential to the browser. Obfuscating the value or adding it only when making a direct fetch doesn’t make it private.
Keep SOLVETHREE_API_KEY in server-only configuration and use it only from trusted server code. The browser should call your application route, never the solvethree task API with your workspace key.
Keep task inputs in a server-only helper
// Import only from trusted server code. Your application authenticates the caller.
import { Solvethree } from '@solvethree/sdk';
const client = new Solvethree({ apiKey: process.env.SOLVETHREE_API_KEY });
export async function createCaptchaForLogin(requestId) {
return client.createTask({
site: process.env.CAPTCHA_SITE,
action: 'login',
proxy: process.env.SOLVETHREE_PROXY,
requestId,
}, { waitMs: 0, signal: AbortSignal.timeout(20000) });
}
// The browser calls your authenticated application route, not the solver API.
// Save the returned task ID; retrieve that ID from server code with getTask.
// Submit a ready token using the proxy/browser identity attached to that solve.
// Merely moving the key server-side does not configure the browser's proxy.Configure SOLVETHREE_API_KEY, CAPTCHA_SITE and SOLVETHREE_PROXY on the server. CAPTCHA_SITE is the target hostname, not a full URL. The helper fixes the action to login and supplies one proxy; the browser doesn’t choose any of those inputs.
Set up an active profile for that hostname in the workspace before calling the helper. Sending site and action doesn’t create one. See site profile setup.
waitMs: 0 requests creation without waiting for the solve to finish. Save the returned task ID and retrieve it from server code with getTask. The helper’s 20-second abort signal bounds that request; it isn’t a promise that solving finishes within 20 seconds.
Expose a login operation, not an arbitrary solver
The helper isn’t an authenticated route by itself. Put these checks around it before accepting browser requests:
| Operation | Browser sends | Backend does |
|---|---|---|
| Start the login solve | The application session and a requestId for this login attempt. | Authenticates the caller, validates the attempt, checks creation limits, and calls the fixed helper. Saves the task ID against that caller and attempt; returns only the task ID and status needed by the UI. |
| Read progress | The application session and the task ID returned for that attempt. | Checks that the task belongs to the caller before retrieving it. Returns progress, a controlled failure response, or the ready result needed by the authorized submitting client. |
Don’t forward a browser-supplied task body. Reject attempts to choose another hostname, action, proxy or task type. Apply a creation limit per caller or application session: an authenticated caller can still drain credit by starting fresh tasks. If the route uses cookie authentication, protect task-creating requests against cross-site request forgery.
A task ID isn’t authorization. Check ownership on every progress request, and don’t return workspace credentials or server proxy credentials in browser responses.
Recover the existing task before creating another
- Keep one requestId for the logical login attempt. Validate its documented format: 1–64 ASCII letters, numbers, underscores or hyphens. Bind it to the caller’s attempt; it isn’t an authentication credential.
- If creation times out, keep that requestId and the original task inputs. Repeating the same request with the same ID recovers the existing task. Changing the inputs while reusing the ID conflicts.
- Handle non-success HTTP responses before interpreting a success payload. For a retrieved task, pending and solving mean keep polling; ready means a solution is available.
Once you have a task ID, retrieve that task rather than starting another solve on each browser refresh or progress request. See API error handling for failure diagnosis.
Decide which client will submit the token
Before returning a ready token to the browser, decide where the target request will run. The submitting client must use the returned userAgent and secChUa identity and the successful proxy for that solve. Moving the workspace key to a backend does not configure the browser’s proxy.
If the browser can’t submit with those settings, handing it the token alone is not a complete integration. Use a submitting client you can configure consistently, without mixing another session’s identity, proxy or target URL. A ready task means solvethree generated a solution—not that the target accepted the subsequent request.
Sources and API reference
Run your first task.
Start with $5 in workspace credit.