Typestate
Sign in

Job applications

People apply for your jobs with a CV, and you move each applicant through the stages of hiring.

For a small company that hires a few people a year.

Resend logo Resend
Start it with your agentStarting from a blueprint does not count against the action changes of your plan.

Start the job applications blueprint on Typestate

Asked you for the address that gets the applications and the address your emails come from.

Started Job applications: 3 record types, 7 web endpoints.

8 actions are compiling.

Deployed to Sandbox. Try it at your Sandbox address.

What your agent does when you start this blueprint

What runs

Each step has 3 parts: who starts it, how it reaches your backend, and the action that answers.

  1. Youpost a job
    Web endpoint, your keyPOST /jobs
    Post a JobGives the job its own address for your careers page.saves a Job
  2. A personapplies with their CV
    Web endpointPOST /jobs/apply
    Apply for a JobKeeps the application and the CV, and emails the applicant and you.saves an Applicationkeeps 1 filesends 2 emails
  3. Youread who applied
    Web endpoint, your keyGET /applications
    List Applications for a JobAnswers each applicant with a link to their CV that works for one hour.reads only
  4. Youmove an applicant to the interview
    Web endpoint, your keyPOST /applications/move
    Move an ApplicationChanges the stage and keeps the move with your note.saves a StageChangechanges an Application
  5. Youturn an applicant down
    Web endpoint, your keyPOST /applications/reject
    Reject an ApplicationKeeps the move and emails the applicant.saves a StageChangechanges an Applicationsends 1 email
  6. The backendsends you the week's applicants on Monday
    TimerEvery Monday at 08:00, Berlin time
    Send the Weekly DigestOne email that names each new applicant.sends 1 email
  • Web endpoint: your website or app calls it
  • Timer: it runs at a set time

The rule you will want to change first

Each move of an applicant is kept with its note, and a hired or rejected applicant moves no further.

It lives in the action Move an Application. Tell your agent how you want it instead: one sentence changes, and you read the change before it goes Live.

What it is made of

The map shows the records it keeps, the actions it runs and the services it calls.

What it keeps

Jobtitle, slug, description, state
Applicationjob, name, email, note, stage
StageChangeapplication, from stage, to stage, note

What it does

Post a Jobweb endpoint, your key
List Open Jobsweb endpoint
Close a Jobweb endpoint, your key
Apply for a Jobweb endpoint
Move an Applicationweb endpoint, your keyThe rule to change first
Reject an Applicationweb endpoint, your key
List Applications for a Jobweb endpoint, your key
Send the Weekly DigestEvery Monday at 08:00, Berlin time

What it reaches

ResendSend email from your own address: sign-in links, receipts, reminders.
Your website7 web endpoints

What it keeps

Job

4 fields

One job you hire for, open or closed.

title
required, at most 200 characters
slug
required, never twice
description
required, at most 10000 characters
state
required, one of open, closed

Everyone sees the jobs. Only your key posts and closes them.

Application

5 fields

One person who applied for a job, with their CV and their stage.

job
required
name
required, at most 200 characters
email
required, an email address
note
optional, at most 5000 characters
stage
required, one of new, screening, interview, offer, hired, rejected

Anyone may apply. Only your key moves applications. The Monday digest reads them too, so the read is open to the actions.

StageChange

4 fields

One move of an application from one stage to the next, with a note.

application
required
from stage
required
to stage
required
note
optional, at most 2000 characters

Only your key reads and writes the moves.

What it does

Open an action to read its steps: each sentence, with the calls that do it under it.

Post a JobYou post a job, and it gets its own address.

Who starts it

From the webPOST /jobs

What it is given

Titletext, needed

Descriptiontext, needed

What it does

  • input.descriptionwhat the caller gives
  • joba value it works out
  • Joba record type
  • Joban attribute of a record or an answer

Create a Job with the given title and description, and slug set to the title written for a web address.

Add a Job

set description to input.description

set slug to the slug of the given title, for a web address

set title to input.title

Answer with that Job's slug.

Answer

slug is job.slug

List Open JobsYour careers page shows the open jobs.

Who starts it

From the webGET /jobs

What it is given

Nothing. It needs no input.

What it does

  • jobsa value it works out
  • Joba record type
  • Joban attribute of a record or an answer
  • "open"a fixed text or number

List the Jobs whose state is open, newest first.

jobs is

Find Jobs where

state is "open"

sorted by created_at, highest first

For each Job, answer its title, slug and description.

rows is

each job of jobs as

description is job.description

slug is job.slug

title is job.title

Answer

jobs is rows

Close a JobYou close a job, and it takes no more applications.

Who starts it

From the webPOST /jobs/close

What it is given

Jobtext, needed

What it does

  • input.jobwhat the caller gives
  • joba value it works out
  • Joba record type
  • Joban attribute of a record or an answer
  • "closed"a fixed text or number
  • refusewhere it says no

Find the Job whose slug is the given job.When none matches, refuse with "No such job".

job is

Find the first Job where

slug is input.job

when none refuse "No such job"

Change that Job's state to closed.

Change the Job where

id is job.id

set state to "closed"

Answer with that Job's slug, and closed as the state.

Answer

slug is job.slug

state is "closed"

Apply for a JobA person applies from your careers page with their CV, and you get an email.

Who starts it

From the webPOST /jobs/apply

What it is given

Jobtext, needed

Nametext, needed

EmailEmail, needed

Notetext, optional

Cva file, needed

What it does

  • input.jobwhat the caller gives
  • joba value it works out
  • Joba record type
  • Joban attribute of a record or an answer
  • "open"a fixed text or number
  • toa part of an email or a payment
  • refusewhere it says no

Find the Job whose slug is the given job and whose state is open.When none matches, refuse with "This job is not open".

job is

Find the first Job where

slug is input.job

state is "open"

when none refuse "This job is not open"

Create an Application with job_id set to that Job's id, and name, email and note set to the given name, email and note.Store the uploaded CV as that Application's cv.

Add an Application

set email to input.email

set job_id to job.id

set name to input.name

set note to input.note

Attaches a file to a record with file = the given cv, id = the application's id, record = "Application", slot = "cv".

Email the given email with the subject "We got your application", saying that Job's title.

Send an email

to input.email

template "Apply for a Job Email"

title job.title

Email the HIRING_EMAIL setting with the subject "New application", saying that Job's title and the given name and email.

Send an email

to the setting HIRING_EMAIL

template "New Application Email"

email input.email

name input.name

title job.title

Answer with that Application's id.

Answer

application is application.id

Move an ApplicationYou move an applicant to the next stage, and the change is kept.

Who starts it

From the webPOST /applications/move

What it is given

Applicationreference to an Application, needed

Stageone of screening, interview, offer, hired, needed

Notetext, optional

What it does

  • input.applicationwhat the caller gives
  • applicationa value it works out
  • Applicationa record type
  • Applicationan attribute of a record or an answer
  • "hired"a fixed text or number
  • refusewhere it says no

Find the Application whose id is the given application.When none matches, refuse with "No such application".When that Application's stage is hired or rejected, refuse with "This application is closed".

application is

Find the first Application where

id is input.application

when none refuse "No such application"

When application.stage is one of "hired" or "rejected"

Refuse "This application is closed"

Create a StageChange with application_id set to that Application's id, from_stage set to its stage, to_stage set to the given stage, and note set to the given note.Change that Application's stage to the given stage.

Add a StageChange

set application_id to application.id

set from_stage to application.stage

set note to input.note

set to_stage to input.stage

Change the Application where

id is application.id

set stage to input.stage

Answer with that Application's id, and the given stage as the stage.

Answer

application is application.id

stage is input.stage

Reject an ApplicationYou turn an applicant down, and they get an email that says so.

Who starts it

From the webPOST /applications/reject

What it is given

Applicationreference to an Application, needed

Notetext, optional

What it does

  • input.applicationwhat the caller gives
  • applicationa value it works out
  • Applicationa record type
  • Applicationan attribute of a record or an answer
  • "hired"a fixed text or number
  • toa part of an email or a payment
  • refusewhere it says no

Find the Application whose id is the given application.When none matches, refuse with "No such application".When that Application's stage is hired or rejected, refuse with "This application is closed".

application is

Find the first Application where

id is input.application

when none refuse "No such application"

When application.stage is one of "hired" or "rejected"

Refuse "This application is closed"

Create a StageChange with application_id set to that Application's id, from_stage set to its stage, to_stage set to rejected, and note set to the given note.Change that Application's stage to rejected.

Add a StageChange

set application_id to application.id

set from_stage to application.stage

set note to input.note

set to_stage to "rejected"

Change the Application where

id is application.id

set stage to "rejected"

Find the Job whose id is that Application's job_id.When none matches, refuse with "No such job".

job is

Find the first Job where

id is application.job_id

when none refuse "No such job"

Email that Application's email with the subject "Your application", saying that Job's title.

Send an email

to application.email

template "Reject an Application Email"

title job.title

Answer with that Application's id, and rejected as the stage.

Answer

application is application.id

stage is "rejected"

List Applications for a JobYou read who applied for a job, with a link to each CV that works for one hour.

Who starts it

From the webGET /applications

What it is given

Jobtext, needed

Stageone of new, screening, interview, offer, hired, rejected, optional

What it does

  • input.jobwhat the caller gives
  • joba value it works out
  • Joba record type
  • Joban attribute of a record or an answer
  • refusewhere it says no

Find the Job whose slug is the given job.When none matches, refuse with "No such job".

job is

Find the first Job where

slug is input.job

when none refuse "No such job"

List the Applications whose job_id is that Job's id, first applied first.When a stage is given, list only the Applications at that stage.

applications is

Find Applications where

job_id is job.id

stage is input.stage

sorted by created_at, lowest first

For each Application, answer its id, name, email, note, stage, created_at as the time it applied, and a link to its cv that works for one hour.

rows is an empty list

For each application in applications

Lists the files of a record with id = the application's id, record = "Application", slot = "cv", as files.

Makes a link to one file with expires in = 3600, file = the id of the first item of the value named files, as cv link.

Adds an object with (application = the application's id, applied at = the application's created at, cv = the value named cv link, email = the application's email, name = the application's name, note = the application's note, stage = the application's stage) to rows.

Answer

applications is rows

Send the Weekly DigestEvery Monday, you get one email with the people who applied in the last 7 days.

Who starts it

A timer, every Monday at 08:00, Berlin time

What it is given

Nothing. It needs no input.

What it does

  • applicationsa value it works out
  • Applicationa record type
  • Applicationan attribute of a record or an answer
  • 7 daysa fixed text or number
  • nowwhat it knows without being told
  • toa part of an email or a payment

List the Applications whose created_at is 7 days ago or later.

applications is

Find Applications where

created_at is on or after now - 7 days

sorted by created_at, lowest first

For each of those Applications, find the Job whose id is its job_id, and keep its name and that Job's title for the email.An Application whose Job is gone is left out.

lines is an empty list

For each application in applications

job is

Find the first Job where

id is application.job_id

when none skip this one

Adds "{the application's name} for {the job's title}" to lines.

When there is at least one, email the HIRING_EMAIL setting with the subject "Applications this week", saying the count and those names.

When number of lines is more than 0

Send an email

to the setting HIRING_EMAIL

template "Send the Weekly Digest Email"

count the number of the value named lines as a number

names the items of the value named lines joined with " "

What it does by itself

  • Every Monday at 08:00, Berlin time, the week's applications come in one email.

Who signs in and what they may read

People sign up and sign in with an email address and a password, or with a provider you add, such as Google.

  • Job: Everyone sees the jobs. Only your key posts and closes them.
  • Application: Anyone may apply. Only your key moves applications. The Monday digest reads them too, so the read is open to the actions.
  • StageChange: Only your key reads and writes the moves.

Web endpoints

What it doesWho may callAddress
Your careers page lists the open jobs.anyoneGET /jobs
Your careers page posts each application here, with the CV.anyonePOST /jobs/apply
Only you, with your key, post a job.you, with your keyPOST /jobs
Only you, with your key, close a job.you, with your keyPOST /jobs/close
Only you, with your key, move an applicant.you, with your keyPOST /applications/move
Only you, with your key, turn an applicant down.you, with your keyPOST /applications/reject
Only you, with your key, read the applications.you, with your keyGET /applications

What it cannot do yet

  • It does not read a CV for you. It keeps the file, and you read it from a link that works for one hour.
  • It does not build the careers page. Your site lists the jobs and posts the form to it.
  • It does not plan interviews. Book them in your own calendar.

Build the pages with your tool

Typestate is the backend. Paste this brief into Lovable, v0, Claude Code or the tool that builds your pages.

Show the brief
Build the pages for "Job applications". The backend runs on Typestate and answers these web endpoints: GET /jobs: Your careers page lists the open jobs. Who may call: anyone. POST /jobs/apply: Your careers page posts each application here, with the CV. Who may call: anyone. POST /jobs: Only you, with your key, post a job. Who may call: you, with your key. POST /jobs/close: Only you, with your key, close a job. Who may call: you, with your key. POST /applications/move: Only you, with your key, move an applicant. Who may call: you, with your key. POST /applications/reject: Only you, with your key, turn an applicant down. Who may call: you, with your key. GET /applications: Only you, with your key, read the applications. Who may call: you, with your key. People sign up at POST /auth/signup and sign in at POST /auth/login, which answers a token. Send it as Authorization: Bearer <token>. A refused call answers a sentence that can be shown to the person as it is.

Start it

  1. Tell your agent the sentence on the right.
  2. It asks you for the address that gets each new application and the Monday digest.
  3. It asks you for the address emails come from, on a domain you verified at Resend, such as "Acme Jobs <jobs@acme.example>".
  4. It tells you what only you can do, such as pasting your Resend key in the cockpit. A key never goes through the agent.
  5. Open your Sandbox address and try it yourself.

Starting from this blueprint does not count against the action changes of your plan. The Free plan runs it on Sandbox. Live needs a paid plan.

Start the job applications blueprint on TypestateChoose a plan

Related blueprints