# Eligibility (/docs/strategies/eligibility)



Recipient eligibility traits decide **who is qualified** to receive a routable —
*before* a routing strategy runs. They are a governed qualification contract, not
another general-purpose annotation: a licensed agent for a regulated product, a
certified installer, a language match for support. Encoding those gates as durable
traits keeps eligibility logic out of ad-hoc rules and makes "why wasn't this
recipient eligible?" answerable.

Traits are administered through the management surface and evaluated during
routing before selection.

<Callout title="Traits are not tags, attributes, or metadata" type="info">
  * **Traits** — the system of record for durable qualifications (skill, license,
    certification, language, territory, product/source permission, compliance).
  * **Tags** — lightweight grouping and migration hints (`region:west`).
  * **Attributes** — route-time values strategies and rules may inspect, but not the
    qualification record.
  * **Metadata** — operational labels. Never put licenses, provider payloads,
    secrets, or PII here.
</Callout>

## Where it runs [#where-it-runs]

Routing evaluates the eligibility **profile** bound to a pool or route before
strategy selection, in this order:

```mermaid
flowchart TD
  H["HARD<br/>remove unqualified recipients"] --> P["PREFERRED<br/>add preference score + feature facts"]
  P --> D["DISALLOWED<br/>eliminate blocked traits"]
  D --> F["FALLBACK<br/>handle a thin or empty set"]
```

The fallback mode is one of **reject**, **queue**, **fallback pool**,
**best-effort**, or **manual review**.

Because preferred requirements emit strategy feature facts, they flow straight
into the [feature snapshot](/docs/strategies/contracts#strategy-feature-snapshot)
under the `eligibility.*` keys — so the strategy that runs next can *prefer*
better-qualified recipients without eligibility having to pick the winner itself.

## Trait kinds [#trait-kinds]

| Kind                        | Example                                                         |
| --------------------------- | --------------------------------------------------------------- |
| `skill`                     | `skill.mortgage`                                                |
| `license`                   | `license.ca-real-estate` (evidence + expiry)                    |
| `certification`             | a certification with evidence and freshness                     |
| `language`                  | `language.es`                                                   |
| `territory`                 | a postal / administrative / radius / polygon / account-list set |
| product / source permission | permission to work a product line or lead source                |
| `compliance`                | a compliance status flag                                        |

```json title="A skill trait definition"
{ "key": "skill.mortgage", "kind": "skill", "value_type": "bool" }
```

```json title="A territory set (postal shape)"
{ "key": "ca-bay-area", "shape_type": "postal", "postal_codes": ["94105", "94107", "94607"] }
```

Territory sets support postal, administrative, radius, polygon, account-list, and
connector-derived coverage, and feed the `eligibility.territory_match` /
`eligibility.territory_distance_km` features so a profile can require in-territory
coverage or prefer closer recipients.

## Connector-derived assignments [#connector-derived-assignments]

A trait assignment can be populated from a connector record through
mapping/import flows:

```json
{
  "source": "connector",
  "source_ref": {
    "provider_key": "hubspot",
    "connection_id": "conn-1",
    "sync_run_id": "sync-run-1",
    "model": "owner",
    "external_id": "owner-123",
    "record_version": "sha256:record"
  }
}
```

The result is still a Ductor trait assignment with its own status, evidence,
expiry, and redaction behavior. If the source record is deleted or revoked, or the
mapping profile drifts, the assignment is marked **stale or revoked** — never
silently deleted.

## Gotchas [#gotchas]

<Callout title="These fail closed by design" type="warn">
  * **Sensitive traits require an explicit redaction policy** on the profile. Without
    one, preflight blocks the profile. API, MCP, readiness, and lineage surfaces
    expose IDs, hashes, counts, and reason codes — never raw sensitive values (see
    `eligibility.redacted_sensitive_trait_count`).
  * **Expired required license/certification traits fail closed** in routing and
    surface as eligibility readiness blockers. Active license and certification
    assignments need evidence refs, evidence hashes, and an expiry timestamp.
  * **Promotion readiness blocks a profile** whose territory requirements leave no
    eligible sampled recipient, or that references archived traits, omits required
    coverage, contains stale connector-derived assignments, or has no sample
    recipients.
</Callout>

## Readiness and promotion [#readiness-and-promotion]

Eligibility evaluation emits a routing reliability fact with dimension
`eligibility` and target kind `eligibility_profile`, carrying profile, pool,
candidate, eligible, eliminated, warning, and issue counts plus redacted issue
codes. A promotion candidate can include an `eligibility_profile` artifact whose
metadata names bounded sample recipients:

```json
{ "sample_recipient_ids": ["recipient-1", "recipient-2"], "environment_id": "prod", "pool_id": "pool-mortgage" }
```

Preflight runs the profile against those samples and blocks promotion on any of
the failure conditions above.

## Migrating from tags [#migrating-from-tags]

Don't reinterpret tags as traits at runtime. Use tags as an input to an explicit
migration preview, then create trait definitions and assignments. Typical
mappings: `region:west` → a territory set, `language:es` → `language.es`,
`license:ca` → a license trait with evidence and expiry. `tier:premium` is usually
metadata or a strategy feature, not eligibility — unless it represents a governed
qualification.

## Related [#related]

<Cards>
  <Card title="Contracts" href="/docs/strategies/contracts">
    Preferred traits feed the eligibility.\* feature facts strategies consume.
  </Card>

  <Card title="Geo strategies" href="/docs/strategies/geo">
    Raw distance-based routing, complementary to territory-set eligibility.
  </Card>

  <Card title="Pipelines" href="/docs/strategies/pipelines">
    Compose an eligibility filter with a selection stage.
  </Card>

  <Card title="Route authoring" href="/docs/management/route-authoring">
    Binding an eligibility profile to a pool or route.
  </Card>
</Cards>
