---
title: "Pre-Qualification Eligibility"
slug: pre-qual-eligibility
section: Integrations
status: stable
description: "A configuration-driven engine that decides whether an applicant may be handed off to Globus — built so Program Operations staff manage the rules as data, with no engineering involvement."
last-updated: 2026-10-07
---

# Pre-Qualification Eligibility

A configuration-driven engine that decides whether an applicant may be handed off to Globus — built so Program Operations staff manage the rules as data, with no engineering involvement.

## Architecture Overview

The eligibility engine runs before an Application is handed off to Globus, while it still lives in HubSpot and no Globus applicant record exists yet. As built, it executes natively inside HubSpot — a single workflow, [System] Pre-Qual Eligibility Engine, with a custom code action — having replaced the earlier external webhook-based evaluation for faster, more reliable execution that operations staff can configure without engineering. It is the single carve-out to the rule that n8n is not a rules engine : the rules are HubSpot-owned and must be evaluated pre-handoff, so the evaluation cannot live in Globus, and n8n carries only the qualified handoff (see Qualified Handoff to Globus below). In-platform eligibility and compliance (on-program, SEVIS, quota) stays Globus-owned — including the Camp Counselor SEVIS eligibility-path derivation (Cat 1 > Cat 2 > Cat 3, with staff override) and the CAMP-001 / CAMP-002 / CAMP-003 add-on gate, which are enforced post-handoff in Globus (see the SEVIS Readiness page).

> **The engine is static; the rules are data.**
>
> The HubSpot workflow that executes the evaluation never changes when policy changes. Bans and seasons are HubSpot records that Program Operations staff create, edit, and deactivate. Shipping a new policy means editing data, not editing — or redeploying — a workflow.

### Two-Tier Evaluation Model

Every evaluation runs in two ordered tiers. Global rules are checked first and short-circuit the evaluation; only if the applicant clears the global tier are the program-specific rules consulted.

## HubSpot Data Objects

Three HubSpot custom objects are the configuration inputs to the engine. The engine reads them read-only and writes only the evaluation outcome back onto the Application.

### Program Ban List

A Ban List record marks a country as ineligible. Its tier is determined by its associations: a record associated to a Country only is a global ban; a record associated to a Country and a Program is a program-specific ban.

| Property | Purpose |
| --- | --- |
| is_active | Boolean. The ban applies only while true. Lifting a ban means setting this false — records are deactivated, not deleted. |
| ban_reason | Human-readable reason surfaced on the disqualification outcome. |
| Country association | Required. Links the ban to a Country object record (not free text). |
| Program association | Optional. Present = program-specific (Tier 2); absent = global (Tier 1). |

### Program Date Window

A Date Window record opens a country for a program between a start and end date, with a minimum program duration. Windows are always country-specific: the same program can open on different dates for different countries, so a window without a Country association is meaningless.

| Property | Purpose |
| --- | --- |
| start_date / end_date | The open window. The relevant applicant date must fall within this range. |
| minimum_duration | Minimum program length the applicant must satisfy for this window. |
| is_active | Boolean. Closing a season means setting this false. |
| Country + Program association | Both required. Windows are scoped per Country + Program. |

### Country (Source of Truth)

The Country custom object (objectTypeId: 2-63632118) is the single source of truth for country data across the ecosystem. Ban List and Date Window records reference Country object records rather than storing country names as text, so that country identity stays consistent everywhere it is used.

### Association & Referential Integrity

> **Associate to records, never free text.**
>
> Ban List and Date Window records must be associated to Country object records (and, for Tier 2, to Program records) — never matched on a typed-in country name. Free-text country values break referential integrity and cause the engine to miss rules silently. The engine resolves rules by association, not by string comparison.

## Evaluation Logic

The engine runs the following sequence for each Application. The first failing check short-circuits and writes the failure reason; clearing all checks yields an eligible outcome.

### Setup — Resolve Program & Country

Resolve the target **Program** from the Application. 
Resolve the applicant **Country** from the Application's country field (see Pending Decisions) to a Country object record. 
### Tier 1 — Global Rules

Check active Ban List records associated to this Country with **no Program association**. 
If any match, disqualify with `Country Globally Banned` and stop. 
### Tier 2 — Program-Specific Rules

Check active Ban List records for this **Program + Country**. Any match disqualifies with `Country Banned from Program`. 
Check active Date Window records for this **Program + Country**. The applicant must satisfy start date, end date, and minimum duration of at least one window. 
No matching window disqualifies with `Outside Country Date Window`. 
### Pass-Through, Multi-Window & Failure Reasons

No windows, date check ON: if the Date Window Check is enabled but no active windows exist for the Program + Country, the engine outputs needs_review rather than qualifying — an enabled check that cannot be evaluated never silently passes (see Master Switches). / No windows, date check OFF: if eligibility_date_window_check_enabled is off, the date check is skipped entirely and imposes no restriction — the absence of windows is irrelevant. / Multi-window: a country may have several active windows (e.g. summer and winter). The applicant passes the date check if any single window satisfies start date, end date, and minimum duration. / Failure reasons are explicit and machine-readable: 'Country Globally Banned', 'Country Banned from Program', 'Outside Country Date Window'.
The engine's execution flow and fail-safe error behaviour are also summarised on the Automation (n8n) page, where it appears in the broader HubSpot &harr; Globus integration context.

## Staff Management Guide

These tasks are performed entirely in HubSpot by Program Operations staff — no engineering involvement is required. Each one is a record edit; the engine picks up the change on its next evaluation.

## Engine Configuration (Operations Guide)

The engine is controlled entirely through the Program object and its associated records — no code changes are required to update rules. On each evaluation it writes two properties back to the Application: prequalification_state (qualified / disqualified / needs_review) and pre_qual_rejection_reason (the specific reason code when disqualified or held for review).

> **Trigger and data gathering.**
>
> The workflow fires whenever an Application is created or its key properties (DOB, citizenship, start/end dates) change. The custom code action then reads the Application properties, looks up its associated Program, and evaluates the applicant against that Program's configured rules in sequence.

### Master Switches

Three boolean properties on every Program record decide which checks the engine performs. If a switch is off (null or false), the engine skips that check entirely.

| Program property | Enables |
| --- | --- |
| eligibility_age_check_enabled | Age Check (min/max age from date of birth). |
| eligibility_ban_check_enabled | Citizenship Ban Check (Tier 1 global + Tier 2 program bans, documented above). |
| eligibility_date_window_check_enabled | Date Window Check (program currently open for the applicant's country). |

> **Fail-safe: enabled but unconfigured yields needs_review.**
>
> If the Date Window Check is enabled but no active windows are configured for the Program + Country, the engine outputs needs_review rather than qualifying the applicant — an enabled check that cannot be evaluated never silently passes.

### Age Check

When eligibility_age_check_enabled is true, the engine reads two number properties on the Program and compares the applicant's exact age — calculated from date_of_birth — against both bounds. To change the policy, edit the number fields directly on the Program record.

| Program property | Purpose |
| --- | --- |
| eligibility_min_age | Minimum allowed age, e.g. 18. |
| eligibility_max_age | Maximum allowed age, e.g. 30. |

### Qualified Handoff to Globus

Once an Application is marked qualified, a separate workflow — [Wildpacks] Pre-Qualified Applicant Handoff to Globus — triggers automatically and sends the application data to the Globus placement system via an n8n webhook. This is the only part of pre-qualification that touches n8n: the evaluation is native HubSpot; n8n is purely the transport for an already-qualified applicant.

## Supporting HubSpot Configuration

The engine depends on several pieces of HubSpot data plumbing delivered alongside it. These keep the canonical fields populated and structured so the checks above can run against trustworthy, association-backed data.

### Copy Application DOB & Citizenship to Contact

The [Wildpacks] Copy Application DOB & Citizenship to Contact workflow (ID: 1838084164) keeps the Contact record in sync with the canonical citizenship and DOB data that originates on the Application.

| Property | Direction | Source → Target |
| --- | --- | --- |
| applicant_dob | Application → Contact | Application.applicant_dob → Contact.date_of_birth__ |
| applicant_country_of_citizenship | Application → Contact | Application.applicant_country_of_citizenship → Contact.country_of_citizenship__ |

**Trigger:** Property change — applicant_dob OR applicant_country_of_citizenship is known on the Application object (2-59396507). The custom code action looks up the associated Contact via the CRM v4 association API and writes both fields. E2E verified 2026-06-17: Application created with DOB 2000-03-15, GB citizenship — Contact updated within ~30 seconds.

> **Direction correction:** The originating ticket described a Contact → Application flow. The actual implementation is Application → Contact — data originates on the Application from form submission.

### country_of_citizenship__ Contact Property

The canonical citizenship field on the HubSpot Contact object.

| Attribute | Value |
| --- | --- |
| Property name | country_of_citizenship__ |
| Label | Country of Citizenship |
| Type | enumeration |
| Options | 196 ISO alpha-2 country codes (AF through ZW) |
| Object | Contact (0-1) |
| Created | March 2026 |

This property is populated by the Copy Application DOB & Citizenship to Contact workflow above after Application creation.

**Legacy fields (deprecated — do not delete until data migration is confirmed complete):**
- `what_is_your_country_of_citizenship` — free text from Lead Ads, NOT canonical
- `country_of_citizenship` (without `__`) — legacy, NOT canonical. Caveat: the `[HubSpot→Globus] Flow 1: Pre-Qualified Applicant Handoff` n8n workflow (the Fetch Associated Contact and Prepare Globus Payload nodes) currently reads `country_of_citizenship` (without `__`) to build `countryOfCitizenship` and never reads `country_of_citizenship__`; the HubSpot property read by that handoff therefore differs from the canonical `country_of_citizenship__`.

### Qualifier Response → Country Object Association

Replaces free-text country_of_residence storage on the Qualifier Response object with a proper association to the Country custom object.

| Attribute | Value |
| --- | --- |
| From object | Qualifier Response (2-62092673) |
| To object | Country (2-63632118) |
| Association Type ID | 277 |
| Label | Country of Residence |
| Inverse Type ID | 278 |
| Inverse label | Qualifier Responses |

**Data flow:** Qualifier Response.country_of_residence association → Country object (ISO alpha-2 code) → Pre-Qual Engine reads country code for eligibility check.

### SWT 2026 Program Date Windows

Date window records loaded into HubSpot for the SWT 2026 program season, sourced from the US State Department 2026 SWT Program Date Chart (Feb 2026).

| Record type | Count | Notes |
| --- | --- | --- |
| Program Date Window records | 120 | One per eligible country |
| Full Ban records | 19 | Countries with full entry restrictions per State Dept |
| Partial Ban records | 20 | Countries with partial restrictions |
| **Total** | **159** | All associated to SWT program (ID: 54481872056) |

**HubSpot object:** Program Date Window (objectTypeId: 2-64290648). Properties: country association, earliest_start_date, latest_end_date, min_duration_weeks, max_duration_weeks, is_active.

**Program scoping:** Date windows are scoped to the SWT program (ID: 54481872056), NOT to CAMP_WP (ID: 55064133731). CAMP_WP applicants are not subject to SWT date window restrictions.

> **E2E verification (2026-06-17):** CAMP_WP test — UK citizen, DOB 1999-07-20 → Pre-Qualification State: Qualified. SWT date window rules did NOT fire. ✅

### Globus → HubSpot Application Mirror Properties

Five Globus mirror properties created on the Application object (2-59396507) to track Globus state alongside HubSpot-native data. These are non-destructive fields that do not overwrite HubSpot-native data.

| Property | Purpose |
| --- | --- |
| globus_applicant_id | Globus applicant UUID |
| globus_application_id | Globus application UUID |
| globus_journey_position | Current journey stage |
| globus_readiness_state | Readiness state from Globus |
| globus_last_sync_at | Timestamp of last Globus sync |

> **Planned work (out of scope for P8):** The full n8n Globus → HubSpot sync workflow (triggered by Globus application events) is a separate implementation task. The mirror properties above are prerequisites.

## Pending Decisions

> **Citizenship vs. country of residence — unresolved.**
>
> The engine currently resolves the applicant country from applicant_country_of_citizenship (ISO alpha-2 code) on the Application object. Whether eligibility should ultimately key off citizenship or country of residence is a pending business decision. The current behaviour is documented here as-is; the field reference will be updated, and this page revised, once the decision is confirmed.

## Related Tickets

The objects and workflow described above are delivered by the following pre-qualification tickets. This page is finalised once those tickets reach Definition of Done.

| Ticket | Asana GID |
| --- | --- |
| [HubSpot] Create Program Ban List custom object | 1215436888027024 |
| [HubSpot] Create Program Date Window custom object | 1215722178296933 |
| [n8n] Build Pre-Qualification Eligibility Engine workflow | 1215437084626921 |
| [HubSpot] Add pre-qualification properties to Application object | 1214743249100300 |
| [HubSpot] Configure Application to Program association | 1215722180028088 |
| [HubSpot] Configure Application to Country association | 1215722302306486 |
| [HubSpot] Pre-Qual Eligibility Engine — Architecture & Operations Guide | 1215799843402329 |
| [P19 Docs] Pre-Qual: HubSpot Workflow — Copy Application DOB & Citizenship to Contact | 1215809351424914 |
| [P19 Docs] Pre-Qual: HubSpot Contact Property — country_of_citizenship__ | 1215809187798188 |
| [P19 Docs] Pre-Qual: Qualifier Response → Country Object association | 1215809190590380 |
| [P19 Docs] Pre-Qual: SWT 2026 Program Date Windows — Data Load & Structure | 1215809351707017 |
| [P19 Docs] Pre-Qual: Globus → HubSpot Application Metadata Sync — Mirror Properties | 1215809190796571 |
