Skip to main content
The whole Auto Apply integration is one loop with two blocking calls:
Your backend supplies answers the candidate approved, or generated under the candidate’s AI acknowledgement, and persists the application ID. No inbound webhook endpoint is needed. Blocking requests can return 202 before work finishes, so handle the recovery path as part of the loop. Resume downloads still need a publicly reachable HTTPS URL. Start with the first-request example. All endpoint paths on this page are relative to https://connect.jobo.world and require the X-Api-Key header.

1. Create blocks until the fields arrive

POST /api/auto-apply/applications holds the connection while Jobo validates the target, acquires a browser, opens the form, and extracts its fields — for up to 90 seconds per request. When the fields are ready, the response contains status: "awaiting_answers" and current_step. This is an abbreviated response; field IDs and deadlines come from your actual application:
Applications that cannot be worked (login walls, CAPTCHAs, unsupported fields) come back from the same call as status: "failed" with a failure code. Keep your HTTP client timeout above 120 seconds: the server holds a blocking call for up to 90 seconds before answering 202 with a snapshot (see Recovery for what to do then).

2. Submit and validate answers

POST /api/auto-apply/applications/{id}/answers takes the complete answer snapshot for the current step. For a step that contains first_name and email, the request body would be:
Save the body as answers.json, set APPLICATION_ID to the returned application ID, and submit it from your backend:
Use the real step’s IDs and values; the example body is not an answer set for every sandbox scenario. Every answer is validated against the step’s typed fields before anything touches the employer’s form. A wrong type, an unknown option, a missing required field — each returns 400 validation_failed immediately, with per-field detail in errors:
A validation failure does not advance the form or consume an ATS correction round. Fix the listed answers and submit again. Always send the current correction_round: although optional, this guard prevents stale answers from being accepted for another round (409 stale_correction_round). Once the answers are accepted, Jobo fills the page and advances the form — intermediate pages auto-continue, the final page auto-submits; the browser session determines the action from the page itself. The call then blocks until one of three outcomes: If processing exceeds the wait budget, the endpoint returns 202 instead. Do not interpret it as a completed step or successful submission.

3. Correction rounds

Client-side validation cannot predict everything the employer’s ATS enforces. When the ATS rejects a value after filling, the same step comes back with correction_round incremented and command_errors describing what was rejected:
Answer it exactly like any other step. After 3 failed rounds the application fails with correction_limit_exceeded.

4. Deadlines

While a step is awaiting answers, a real browser is holding the employer’s form open. Each answerable step carries answers_expire_at — about 5 minutes, capped at 90 seconds for one-time verification-code steps. Treat the returned timestamp as authoritative, rather than calculating a deadline from these defaults. Providers that support resuming can release the browser and keep waiting (answers_expire_at becomes null). Providers that cannot resume fail with answers_timeout; verification-code steps fail with verification_timeout. Answer promptly: a resumed application must reopen the form, and a null deadline does not guarantee unlimited application lifetime. A verification code is an ordinary answer — read it from the candidate’s inbox however you already do and submit it like any other. If you would rather not build inbox access, connected mailboxes are an optional convenience that resolves the code (or confirmation link) for you; nothing here requires them.

5. Recovery

Every blocking call can lose its connection — a deploy, a proxy timeout, a crashed process. Recovery is built into the contract:
  • Create dropped? Repeat the same POST with the same Idempotency-Key. The replay re-attaches to the in-flight application and resumes blocking on the same wait. A different body with a reused key returns 409 idempotency_key_reuse. Persist the key before sending create and the application ID as soon as it is known. Keys expire after 24 hours: do not replay an old create after that window, because it can create another application.
  • Answers dropped? Fetch the application and compare current_step.id, sequence, and correction_round with your saved step. If it is still the same unanswered step, resend its complete snapshot with correction_round. While answers for the current round are already accepted, a duplicate post attaches to the in-flight wait and ignores the new payload. Once a new step or correction round appears, build answers for that state instead. correction_round guards the round; it does not identify a step across pages.
  • Got a 202? The server’s 90-second hold budget expired while the application was still working. The body is a snapshot; continue with GET /api/auto-apply/applications/{id}?wait_seconds=90, looping until the application is answerable or terminal.
The TypeScript SDK retries transient request failures, and run() re-attaches to 202 snapshots. Your application still owns persistence and recovery after a process restart. Run at most one answer-producing worker per application so two callers do not answer different snapshots concurrently. A recovery request looks like this (set APPLICATION_ID to the saved ID):
Keep waiting for queued or running, answer awaiting_answers, and stop for submitted, failed, or canceled. A network failure is never proof that the application failed or that the employer did not receive it.

6. Local development

Run your integration anywhere — localhost, CI, a notebook. Nothing about the loop requires inbound connectivity. The one exception: file field answers (resumes) are supplied as a public HTTPS URL that Jobo downloads. Host the file anywhere Jobo can reach — object storage with a signed URL works well. A localhost URL is rejected with unsafe_file_url.

7. Cancel

POST /api/auto-apply/applications/{id}/cancel returns 202 with a summary and requests cancellation. Fetch the application until it is terminal. Queued applications cancel immediately; executing ones stop at the next safe checkpoint. A submission the ATS already confirmed wins the race — the application ends submitted, with cancel_requested: true recording the attempt.