---
title: "Recruiterflow: the form is in the page, the work is in the widgets"
slug: "recruiterflow"
language: "en"
date: "2026-10-28T21:59:18.784Z"
updated: "2026-10-28T21:59:18.000Z"
description: "Recruiterflow declares its whole form as JSON in the posting page. Reading it is easy. Filling its React widgets is not."
canonical: "https://atsfill.slopit.io/recruiterflow/"
tags: ["adapters", "recruiterflow"]
---

Recruiterflow is the hiring system recruiting agencies use. Every posting lives at `recruiterflow.com/<employer>/jobs/<id>`, and freehire lists about 6,900 of them. That count is why it got an adapter next.

## The form is already in the page

Most hiring systems make you drive the page to learn what it asks. Recruiterflow hands it over. Each posting page carries one JSON object, `var convertedToJSON = {...}`, and a React app draws the form from it. The object has three lists, in the order the page draws them: personal information, a candidate profile and the employer's questions. Each item says whether it is mandatory, optional or off.

So `atsfill read` doesn't need a browser here. It fetches the page over HTTP and parses the JSON. The browser path takes the same HTML and runs it through the same parser. Two ways of reading a form usually drift apart, and here they can't, because there is only one parser.

Finding the control for a question is the one place the JSON doesn't help. The page draws one block per question in the JSON's order, so the adapter finds a question's control by its position. If the number of drawn blocks differs from the number in the JSON, the read fails instead of guessing.

## Filling is the hard part

Reading took an afternoon. Filling took the rest. The form is built from React widgets, and each one wants its own handling:

- Dropdowns are react-select. The adapter opens one and clicks the option with the answer's text. The country list is long, so it types the country first to narrow it.
- Yes/No questions are two buttons, and the picked one gets a `selected` class.
- Work history and education take a month picker that accepts `Jan 2020` typed, then Tab.
- Salary and number ranges are a Min and a Max input.
- One question type is an accordion of groups with a checkbox per item. One employer out of 43 used it. It still had to work.
- Some employers add footer notes with a consent checkbox the page won't submit without.

The résumé goes first, and that order matters. Recruiterflow uploads a file the moment you attach it, parses the résumé and fills any empty field with what it finds, last name included. Upload it after typing the answers and the parse can overwrite them. Upload it first and the answers land on top.

One employer's page drew a whole section collapsed, its inputs hidden. The first version of the adapter tried to type into inputs nobody could see. It now opens a collapsed section by its header before filling it. That bug turned up in the gate's sample, 1 of 10 forms, which is the point of running the gate on real postings.

## What was checked

I looked at 43 employers' forms and wrote down every rendering they use and how often. Name, email and phone show up on nearly all of them. A date question showed up on none, but the JSON can declare one, so the adapter handles it anyway.

Before an adapter counts as supported it has to pass a gate. For Recruiterflow that meant 15 employers in the sample, all dry-running to `ready`. Ten more postings outside the sample had to dry-run `ready` too, and five had to give the same fields from the HTTP read and the browser read.

No application has gone out through it yet. Submit posts to the page's own `submit-application` endpoint, and atsfill keeps that response as evidence unless it says `FAILURE`. Until a real send happens, Recruiterflow sits with the systems verified by dry run.
