# API Contract: Policy Builder Constraint Methods

**Branch**: `003-credential-filtering-api` | **Date**: 2026-01-21

## Overview

This document defines the API contract for new constraint methods added to policy builders. All methods follow the fluent builder pattern and are available on the `<Incomplete>` state.

## Common Methods (All Policy Builders)

### `with_issuer_did`

Constrain the credential to be issued by a specific DID.

```rust
/// Require the credential issuer to match a specific DID.
///
/// # Arguments
/// * `did` - The issuer DID (must start with "did:")
///
/// # Example
/// ```rust,no_run
/// use lastid_sdk::{BaseCredentialPolicy, PolicyBuilder};
///
/// let policy = BaseCredentialPolicy::new()
///     .with_issuer_did("did:lastid:trusted-issuer")
///     .with_callback("https://example.com/callback");
///
/// let request = policy.to_request().unwrap();
/// assert!(request.constraints.is_some());
/// ```
///
/// # Errors
/// Returns `PolicyValidation` error in `to_request()` if DID format is invalid.
#[must_use]
pub fn with_issuer_did(mut self, did: impl Into<String>) -> Self
```

**Constraint Generated**: `{ "iss": did }`

---

### `with_subject_did`

Constrain the credential subject to a specific DID.

```rust
/// Require the credential subject to match a specific DID.
///
/// Useful for re-authentication flows where the RP already knows the user's DID.
///
/// # Arguments
/// * `did` - The subject DID (must start with "did:")
///
/// # Example
/// ```rust,no_run
/// use lastid_sdk::{PersonaPolicy, PolicyBuilder};
///
/// let policy = PersonaPolicy::new()
///     .with_subject_did("did:lastid:known-user-123")
///     .with_callback("https://example.com/callback");
/// ```
///
/// # Errors
/// Returns `PolicyValidation` error in `to_request()` if DID format is invalid.
#[must_use]
pub fn with_subject_did(mut self, did: impl Into<String>) -> Self
```

**Constraint Generated**: `{ "sub": did }`

---

### `with_claim_constraint`

Add an arbitrary claim constraint.

```rust
/// Add a generic constraint requiring a claim field to equal a specific value.
///
/// # Arguments
/// * `field` - The claim field path (e.g., "company", "lid.verification.level")
/// * `value` - The required value (will be serialized as JSON)
///
/// # Example
/// ```rust,no_run
/// use lastid_sdk::{PersonaPolicy, PolicyBuilder};
///
/// let policy = PersonaPolicy::new()
///     .with_claim_constraint("visibility", serde_json::json!("public"))
///     .with_callback("https://example.com/callback");
/// ```
#[must_use]
pub fn with_claim_constraint(
    mut self,
    field: impl Into<String>,
    value: serde_json::Value,
) -> Self
```

**Constraint Generated**: `{ field: value }`

---

## PersonaPolicy / VerifiedPersonaPolicy Methods

### `with_email_domain`

Filter by email domain pattern.

```rust
/// Require the email field to match a specific domain.
///
/// Uses suffix matching: "acme.com" matches "user@acme.com" and "user@sales.acme.com".
///
/// # Arguments
/// * `domain` - The domain to match (e.g., "acme.com"), without @ symbol
///
/// # Example
/// ```rust,no_run
/// use lastid_sdk::{PersonaPolicy, PolicyBuilder};
///
/// let policy = PersonaPolicy::new()
///     .with_email_domain("bigcorp.com")
///     .with_callback("https://example.com/callback");
/// ```
///
/// # Errors
/// Returns `PolicyValidation` error in `to_request()` if domain contains @ or /.
#[must_use]
pub fn with_email_domain(mut self, domain: impl Into<String>) -> Self
```

**Constraint Generated**: `{ "email_domain": domain }` (IDP maps to pattern filter)

---

## EmploymentPolicy Methods

### `with_employer_did`

Require credentials from a specific employer.

```rust
/// Require the employment credential to be issued by a specific employer DID.
///
/// # Arguments
/// * `did` - The employer's DID (must start with "did:")
///
/// # Example
/// ```rust,no_run
/// use lastid_sdk::{EmploymentPolicy, PolicyBuilder};
///
/// let policy = EmploymentPolicy::new()
///     .with_employer_did("did:lastid:acme-corp")
///     .with_callback("https://example.com/callback");
/// ```
#[must_use]
pub fn with_employer_did(mut self, did: impl Into<String>) -> Self
```

**Constraint Generated**: `{ "employer_did": did }`

---

## TrustPolicy Methods

### `with_minimum_level`

Require a minimum trust level.

```rust
/// Require the trust credential to have at least the specified level.
///
/// # Arguments
/// * `level` - Minimum trust level (0-5, where 5 is highest)
///
/// # Example
/// ```rust,no_run
/// use lastid_sdk::{TrustPolicy, PolicyBuilder};
///
/// let policy = TrustPolicy::new()
///     .with_minimum_level(3)
///     .with_callback("https://example.com/callback");
/// ```
///
/// # Errors
/// Returns `PolicyValidation` error in `to_request()` if level is negative.
#[must_use]
pub fn with_minimum_level(mut self, level: i64) -> Self
```

**Constraint Generated**: `{ "level_minimum": level }` (IDP maps to minimum filter)

---

## AgeProofPolicy Methods

### `with_minimum_age`

Convenience wrapper for age verification.

```rust
/// Require the age proof credential to verify at least the specified age.
///
/// # Arguments
/// * `years` - Minimum age in years (1-150)
///
/// # Example
/// ```rust,no_run
/// use lastid_sdk::{AgeProofPolicy, PolicyBuilder};
///
/// let policy = AgeProofPolicy::new()
///     .with_minimum_age(21)
///     .with_callback("https://example.com/callback");
/// ```
///
/// # Errors
/// Returns `PolicyValidation` error in `to_request()` if age is outside 1-150 range.
#[must_use]
pub fn with_minimum_age(mut self, years: u8) -> Self
```

**Constraint Generated**: `{ "age_minimum": years }` (IDP maps to minimum filter)

---

## Constraint Mapping Reference

| SDK Method | Constraint Key | IDP Filter Type |
|------------|---------------|-----------------|
| `with_issuer_did(did)` | `iss` | `{ const: did }` |
| `with_subject_did(did)` | `sub` | `{ const: did }` |
| `with_email_domain(domain)` | `email_domain` | `{ pattern: ".*@{domain}$" }` |
| `with_employer_did(did)` | `employer_did` | `{ const: did }` |
| `with_minimum_level(n)` | `level_minimum` | `{ minimum: n }` |
| `with_minimum_age(years)` | `age_minimum` | `{ minimum: years }` |
| `with_claim_constraint(k, v)` | `{k}` | `{ const: v }` |
