Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

atsfill

Let your agent apply for you. One CLI for every hiring system.

PyPI Python CI License


Install • Quickstart • How it works • Docs


atsfill reads the application form behind a job posting, fills it with your answers, and checks what the hiring system will receive before anything is sent. It is built for agents with a shell: every command prints one JSON document and exits with a code a script can branch on.

  • read lists a form's fields with their ids, types, options and whether they are required.
  • fill is a dry run: it fills the form, reads it back field by field, and never submits.
  • send fills again and submits only when every answer reads back as given.
  • Submitting is off until you enable it per hiring system, in a config file the agent does not write.
  • A posting already sent is refused unless you pass --resend.

Supported hiring systems

oracle run history smartrecruiters run history greenhouse run history manatal run history paylocity run history dayforce run history workable run history ashby run history bamboohr run history teamtailor run history jazzhr run history lever run history hireology run history paycor run history zohorecruit run history phenom run history breezy run history recruitee run history rippling run history personio run history eightfold run history pinpoint run history hiringthing run history recruiterflow run history jobaffinity run history welcomekit run history join run history workatastartup run history wttj run history workday run history successfactors run history adpmyjobs run history isolvedhire run history jobstreet run history gupy run history ukg run history

Each line is a hiring system atsfill has an adapter for, and its run history: the last 30 days of runs, one mark per posting and version, newest first.

run history legend

The systems lists which ones need an account, to read the form or only to send it. Any other system returns unsupported, so the consumer can fall back to driving a browser itself.

Why atsfill?

An agent applying through a generic browser carries each system's quirks as prose, rereads it every run, and checks the form before submitting only because an instruction says so. atsfill moves the quirks into one adapter per system and makes the check code: a send whose read-back differs from the answers is refused.

Generic browser agent atsfill
System quirks prose in the prompt an adapter per system
Check before submit an instruction the read-back must match
Permission to submit whatever the agent decides a config file, per system
Double send possible refused without --resend
Output page snapshots one JSON result

Install

atsfill runs on Linux, macOS and Windows with Python 3.14, which uvx fetches if needed.

uvx atsfill install-browser

This downloads the Camoufox browser build atsfill is pinned to: 1.3 GB, about 2.5 GB once unpacked.

Every push to main that passes CI is also published as a dev release; uvx --prerelease allow atsfill runs the newest.

To teach your agent the CLI, add its skill, which runs the newest dev release:

npx skills add shbernal/atsfill

Quickstart

  1. List the form's fields:

    uvx atsfill read https://job-boards.greenhouse.io/acme/jobs/1234
    
  2. Write your answers to answers.json, keyed by field id. Files take an absolute path:

    {
      "first_name": "Ada",
      "last_name": "Lovelace",
      "email": "ada@example.com",
      "resume": "/home/ada/resume.pdf"
    }
    
  3. Dry-run the fill. It ends ready when every answer reads back as given:

    uvx atsfill fill https://job-boards.greenhouse.io/acme/jobs/1234 --answers answers.json
    
  4. Allow the system to submit in ~/.config/atsfill/config.toml:

    submit-allowed = ["greenhouse"]
    
  5. Send. A submitted result carries the evidence of the send:

    uvx atsfill send https://job-boards.greenhouse.io/acme/jobs/1234 --answers answers.json
    

A dry run is not invisible: some systems save answers or the resume as they are typed. atsfill status lists each system's side effects, whether it is allowed to submit, and its scope: the systems.

One home can send for more than one user: --user <name> gives each their own sessions, double-send guard and [users.<name>] section in the config file (ADR 0015). Given an account's email (atsfill login <adapter> --email) and a password source (a [password-commands] entry such as rbw get example.com {email}, or --password - on stdin), atsfill signs in by itself, and again whenever a form command finds the session signed out. When a system emails an OTP, on a sign-in, a sign-up or a form, atsfill gets it from the user's [otp-commands] entry, a command that reads the mailbox ({email} and {since} are filled in), and types it in; without one the command ends blocked: no_otp_command. Where a system's sign-up allows it, atsfill login <adapter> --create --email <address> (with --details <file.json> where the sign-up asks the user's name, phone or address) creates the account with a password atsfill generates and keeps in the system's secret store, never printed; creating an account is a side effect, never part of fill or send. Where a system applies only from a complete account profile, atsfill profile <adapter> reads its profile form and atsfill profile <adapter> --answers <file.json> saves it, step by step, skipping the steps that already match; a form command that meets an incomplete profile ends blocked: profile_incomplete, naming that command. Saving a profile is a side effect, never part of fill or send (ADR 0017).

How it works

flowchart LR
    CLI[atsfill CLI] --> Core
    Core -->|detect, read, fill, read back, submit| Adapter[adapter for the system]
    Adapter --> Browser[headless Camoufox]
    Core --> SubmitGate{read-back matches,<br>submit enabled,<br>not already sent}
    SubmitGate -->|yes| Submit[submit]
    SubmitGate -->|no| Refused[refused]

Each command launches its own headless Camoufox, a Firefox build patched to hide automation, with a fresh context and one saved device identity. The core owns every rule about when a send is allowed; an adapter only knows its system's page.

Every result has a status (ready, refused, submitted, blocked, unknown, unsupported) and, when it is not clean, a reason code. The exit code is 0 for ready and submitted, 1 otherwise, 2 for a usage error.

Each read, fill and send sends one anonymous event (the system, the command, the status, a hash of the posting, never answers or URLs); atsfill report sends a failed run's URL and page, scrubbed of your answers, when you ask: what is sent and how to turn it off.

More

  • Architecture decisions: the contract, the submit gate, the browser, sessions.
  • Hiring systems: every system mapped so far, in backlog order, and each one's form as observed.
  • atsfill --help for every command and option.

Metadata

Release files for atsfill 0.0.5.dev171

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for atsfill 0.0.5.dev171
File Size Uploaded Provenance Actions
atsfill-0.0.5.dev171.tar.gz 295.8 kB Without provenance Details for atsfill-0.0.5.dev171.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for atsfill 0.0.5.dev171
File Interpreter ABI Platform Actions
atsfill-0.0.5.dev171-py3-none-any.whl Python 3 none any Details for atsfill-0.0.5.dev171-py3-none-any.whl

Total release size: 645.0 kB

Release files / atsfill-0.0.5.dev171.tar.gz

Download URL atsfill-0.0.5.dev171.tar.gz
Size 295.8 kB
Tags Source
SHA-256 checksum
How to use checksums
a66f44e55f2e06c9a552ada4fceaa1a8855122d02cfee9b4e3c193309ba38d60
BLAKE2b-256 checksum
How to use checksums
183c1ec2ccff8d9546b29c7f51d3deaba41e1a2aadbd9cb3ab6bf28d4de4d68b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / atsfill-0.0.5.dev171-py3-none-any.whl

Download URL atsfill-0.0.5.dev171-py3-none-any.whl
Size 349.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3bb12eb495b0b3731ab8bc67eb493c843ead28703c17fc6da06c501fbc880557
BLAKE2b-256 checksum
How to use checksums
9639edea1af9a85c923ad0e0e61c4319b27852f4f7f57683bd44876ebb30b781
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for atsfill 0.0.5.dev171

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page