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:
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:
answers.json, set APPLICATION_ID to the returned application
ID, and submit it from your backend:
400 validation_failed immediately, with
per-field detail in errors:
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 withcorrection_round incremented and command_errors describing what was
rejected:
correction_limit_exceeded.
4. Deadlines
While a step is awaiting answers, a real browser is holding the employer’s form open. Each answerable step carriesanswers_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
POSTwith the sameIdempotency-Key. The replay re-attaches to the in-flight application and resumes blocking on the same wait. A different body with a reused key returns409 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, andcorrection_roundwith your saved step. If it is still the same unanswered step, resend its complete snapshot withcorrection_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_roundguards 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 withGET /api/auto-apply/applications/{id}?wait_seconds=90, looping until the application is answerable or terminal.
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):
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.
