# Benefits Navigator — Claude API Integration
**Provider:** Anthropic Claude  
**Recommended Model:** `claude-sonnet-4-6`  
**Base URL:** `https://api.anthropic.com/v1/messages`  
**API Version Header:** `anthropic-version: 2023-06-01`

---

## Project Role: Where Claude Is Used

| Section | Claude Task |
|---|---|
| Eligibility Assessment | Deep multi-program analysis from raw user inputs |
| Benefits Explanation | Plain-language explanation of legal/policy program rules |
| Document Guidance | Explain exactly what docs are needed and how to prepare them |
| Application Chat Support | Answer complex questions during the application process |
| Denial Appeal Letters | Draft formal appeal letters when benefits are denied |

Claude excels at **long-context reasoning and policy interpretation** — ideal for determining eligibility across multiple overlapping programs from a single intake form.

---

## Section 1 — Eligibility Assessment

### System Prompt

```
You are a government benefits eligibility specialist for the United States. 
Your job is to analyze a household's situation and determine which federal and state 
assistance programs they likely qualify for.

Return ONLY valid JSON. No preamble, no explanation outside the JSON.

For each program, include:
- program_code (string, e.g. "SNAP", "MEDICAID", "WIC")
- program_name (string, full name)
- category (string: food|health|housing|childcare|utilities|education|employment)
- is_eligible (boolean)
- confidence_score (integer 0-100)
- estimated_monthly_benefit_min (integer, USD)
- estimated_monthly_benefit_max (integer, USD)
- eligibility_reason (string, 1-2 sentences plain English)
- priority (string: apply_now|high|medium|low)
- apply_url (string, official application URL or empty string)
- documents_needed (array of strings)

Return JSON object: { "programs": [...], "total_estimated_monthly": integer, "summary": "1-2 sentence summary" }
```

### PHP Code — Eligibility Check

```php
<?php
// File: /api/claude/eligibility.php

class ClaudeEligibilityService {
    private string $apiKey;
    private string $model = 'claude-sonnet-4-6';
    private string $baseUrl = 'https://api.anthropic.com/v1/messages';

    public function __construct(string $apiKey) {
        $this->apiKey = $apiKey;
    }

    public function checkEligibility(array $userInputs): array {
        $userMessage = $this->buildEligibilityPrompt($userInputs);

        $payload = [
            'model'      => $this->model,
            'max_tokens' => 4000,
            'system'     => $this->getSystemPrompt(),
            'messages'   => [
                ['role' => 'user', 'content' => $userMessage]
            ]
        ];

        $response = $this->callAPI($payload);
        $text     = $response['content'][0]['text'] ?? '';

        // Strip any accidental markdown fences
        $text = preg_replace('/```json\s*|\s*```/', '', trim($text));

        $result = json_decode($text, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new RuntimeException('Claude returned invalid JSON: ' . $text);
        }

        return $result;
    }

    private function buildEligibilityPrompt(array $d): string {
        return "Analyze eligibility for this household:\n\n" . json_encode([
            'state'             => $d['state'] ?? 'WA',
            'household_size'    => (int)($d['household_size'] ?? 1),
            'annual_income'     => (float)($d['annual_income'] ?? 0),
            'monthly_income'    => round((float)($d['annual_income'] ?? 0) / 12, 2),
            'ages'              => $d['ages'] ?? [],
            'employment_status' => $d['employment_status'] ?? 'employed',
            'has_disability'    => (bool)($d['has_disability'] ?? false),
            'has_children'      => (bool)($d['has_children'] ?? false),
            'pregnant'          => (bool)($d['pregnant'] ?? false),
            'rent_monthly'      => (float)($d['rent_monthly'] ?? 0),
            'utilities_monthly' => (float)($d['utilities_monthly'] ?? 0),
            'citizenship'       => $d['citizenship'] ?? 'citizen',
        ], JSON_PRETTY_PRINT);
    }

    private function getSystemPrompt(): string {
        return 'You are a government benefits eligibility specialist for the United States. '
            . 'Analyze the household situation and determine which federal and state assistance programs '
            . 'they likely qualify for. Return ONLY valid JSON with this structure: '
            . '{ "programs": [ { "program_code": string, "program_name": string, "category": string, '
            . '"is_eligible": boolean, "confidence_score": integer, '
            . '"estimated_monthly_benefit_min": integer, "estimated_monthly_benefit_max": integer, '
            . '"eligibility_reason": string, "priority": string, "apply_url": string, '
            . '"documents_needed": array } ], "total_estimated_monthly": integer, "summary": string }. '
            . 'No markdown, no text outside JSON.';
    }

    private function callAPI(array $payload): array {
        $ch = curl_init($this->baseUrl);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => json_encode($payload),
            CURLOPT_HTTPHEADER     => [
                'Content-Type: application/json',
                'x-api-key: ' . $this->apiKey,
                'anthropic-version: 2023-06-01',
            ],
            CURLOPT_TIMEOUT => 60,
        ]);

        $body   = curl_exec($ch);
        $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($status !== 200) {
            throw new RuntimeException("Claude API error $status: $body");
        }

        return json_decode($body, true);
    }
}

// ---- Usage ----
// $claude = new ClaudeEligibilityService($_ENV['ANTHROPIC_API_KEY']);
// $result = $claude->checkEligibility([
//     'state'          => 'WA',
//     'household_size' => 2,
//     'annual_income'  => 19000,
//     'has_children'   => false,
// ]);
// echo json_encode($result);
?>
```

### Expected JSON Response

```json
{
  "programs": [
    {
      "program_code": "SNAP",
      "program_name": "Supplemental Nutrition Assistance Program",
      "category": "food",
      "is_eligible": true,
      "confidence_score": 95,
      "estimated_monthly_benefit_min": 380,
      "estimated_monthly_benefit_max": 548,
      "eligibility_reason": "Household of 2 with $19,000 annual income is well below the 130% FPL limit of $26,973 for 2 people.",
      "priority": "apply_now",
      "apply_url": "https://www.washingtonconnection.org/WAPPLE/",
      "documents_needed": ["Photo ID", "Recent pay stubs", "Proof of WA address", "Social Security numbers"]
    }
  ],
  "total_estimated_monthly": 548,
  "summary": "This household qualifies for SNAP benefits at near-maximum levels. Apply immediately as approval typically takes 7-30 days."
}
```

---

## Section 2 — Benefits Explanation (Plain Language)

### System Prompt

```
You are a benefits counselor who explains government assistance programs in plain English.
Your audience may have limited education or be stressed about finances.
Use short sentences. Avoid jargon. Be warm and encouraging.
Format your response as clean HTML paragraphs (no markdown).
Never use the word "navigate" or "utilize".
```

### PHP Code — Program Explanation

```php
<?php
class ClaudeExplainerService {
    private ClaudeEligibilityService $claude; // reuse the API caller

    public function explainProgram(string $programCode, array $userContext): string {
        $payload = [
            'model'      => 'claude-sonnet-4-6',
            'max_tokens' => 1000,
            'system'     => 'You are a benefits counselor who explains government assistance programs '
                          . 'in plain English. Use short sentences. Be warm, clear, and encouraging. '
                          . 'Return clean HTML paragraphs only — no markdown, no headers, no lists.',
            'messages'   => [[
                'role'    => 'user',
                'content' => "Explain the $programCode program to someone who:"
                           . "\n- Lives in {$userContext['state']}"
                           . "\n- Has a household of {$userContext['household_size']} people"
                           . "\n- Earns about \${$userContext['annual_income']}/year"
                           . "\n\nExplain: What is it? How much will they get? "
                           . "What happens after approval? Keep it under 150 words."
            ]]
        ];

        // Call API same as above, return text block
        $response = $this->callAPI($payload);
        return $response['content'][0]['text'] ?? '';
    }
}
?>
```

---

## Section 3 — Document Guidance

### System Prompt

```
You are a document preparation specialist for government benefit applications.
For each document type, explain: what it is, what it must show, common mistakes to avoid,
and how to get it if the person doesn't have it.
Return ONLY a JSON array of document objects.
```

### PHP Code — Document Guidance

```php
<?php
function getDocumentGuidance(string $apiKey, string $documentName, string $programCode): array {
    $prompt = "For the program $programCode, explain the document requirement: \"$documentName\"\n\n"
            . "Return JSON object with:\n"
            . "- what_it_is: string (1 sentence)\n"
            . "- must_show: array of strings (what the document must include)\n"
            . "- accepted_types: array of strings (list of acceptable document types)\n"
            . "- common_mistakes: array of strings\n"
            . "- how_to_get: string (if person does not have it)\n"
            . "- sample_tip: string (1 pro tip for photographing/uploading)\n"
            . "No text outside JSON.";

    $payload = [
        'model'      => 'claude-sonnet-4-6',
        'max_tokens' => 800,
        'system'     => 'You are a document preparation specialist for government benefit applications. '
                      . 'Return only valid JSON.',
        'messages'   => [['role' => 'user', 'content' => $prompt]]
    ];

    $ch = curl_init('https://api.anthropic.com/v1/messages');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => json_encode($payload),
        CURLOPT_HTTPHEADER     => [
            'Content-Type: application/json',
            "x-api-key: $apiKey",
            'anthropic-version: 2023-06-01',
        ],
    ]);
    $body = curl_exec($ch);
    curl_close($ch);

    $data = json_decode($body, true);
    $text = $data['content'][0]['text'] ?? '{}';
    $text = preg_replace('/```json\s*|\s*```/', '', trim($text));
    return json_decode($text, true) ?? [];
}
?>
```

---

## Section 4 — Application Chat Support (Multi-Turn)

This is the live Q&A chatbot for users who have questions while applying.

### System Prompt

```
You are a friendly benefits application assistant. You help people apply for government 
assistance programs — specifically {PROGRAM_NAME} in {STATE}.

Context about this user:
- Household size: {HOUSEHOLD_SIZE}
- Annual income: ${ANNUAL_INCOME}
- Estimated benefit: ${ESTIMATED_BENEFIT}/month
- Currently applying at: {APPLY_URL}

Rules:
1. Give short, direct answers (3-5 sentences max)
2. If you don't know something specific, say "I'd recommend calling your local DSHS office"
3. Never give legal advice
4. Never make promises about approval
5. Always be encouraging and non-judgmental
6. When relevant, remind them to have their documents ready
```

### PHP Code — Multi-Turn Chat

```php
<?php
class ClaudeChatService {
    private string $apiKey;
    private string $model = 'claude-sonnet-4-6';

    public function __construct(string $apiKey) {
        $this->apiKey = $apiKey;
    }

    public function chat(
        array $conversationHistory,
        string $newUserMessage,
        array $programContext
    ): string {
        // Build system prompt with context
        $systemPrompt = strtr(
            'You are a friendly benefits application assistant helping someone apply for {PROGRAM_NAME} in {STATE}. '
          . 'User context: household of {HOUSEHOLD_SIZE}, income ~${ANNUAL_INCOME}/year, '
          . 'estimated benefit ${ESTIMATED_BENEFIT}/month. '
          . 'Give short helpful answers (3-5 sentences). Never promise approval. '
          . 'If unsure, suggest calling local DSHS office. Be warm and encouraging.',
            [
                '{PROGRAM_NAME}'       => $programContext['program_name'],
                '{STATE}'              => $programContext['state'],
                '{HOUSEHOLD_SIZE}'     => $programContext['household_size'],
                '{ANNUAL_INCOME}'      => number_format($programContext['annual_income']),
                '{ESTIMATED_BENEFIT}'  => $programContext['estimated_benefit'],
            ]
        );

        // Append new user message to history
        $messages   = $conversationHistory;
        $messages[] = ['role' => 'user', 'content' => $newUserMessage];

        $payload = [
            'model'      => $this->model,
            'max_tokens' => 500,
            'system'     => $systemPrompt,
            'messages'   => $messages,
        ];

        $ch = curl_init('https://api.anthropic.com/v1/messages');
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => json_encode($payload),
            CURLOPT_HTTPHEADER     => [
                'Content-Type: application/json',
                'x-api-key: ' . $this->apiKey,
                'anthropic-version: 2023-06-01',
            ],
            CURLOPT_TIMEOUT => 30,
        ]);

        $body = curl_exec($ch);
        curl_close($ch);

        $data = json_decode($body, true);
        return $data['content'][0]['text'] ?? 'Sorry, I could not process that. Please try again.';
    }

    /**
     * Save conversation turn to DB after each exchange
     * Returns updated history array for next call
     */
    public function appendAndSave(
        array $history,
        string $userMsg,
        string $assistantMsg,
        int $sessionId,
        \PDO $db
    ): array {
        $history[] = ['role' => 'user',      'content' => $userMsg];
        $history[] = ['role' => 'assistant', 'content' => $assistantMsg];

        // Persist to DB
        $stmt = $db->prepare(
            'INSERT INTO chat_messages (session_id, role, content, ai_provider, model_used) 
             VALUES (:sid, :role, :content, "claude", :model)'
        );
        foreach ([
            ['role' => 'user',      'content' => $userMsg],
            ['role' => 'assistant', 'content' => $assistantMsg],
        ] as $msg) {
            $stmt->execute([
                ':sid'     => $sessionId,
                ':role'    => $msg['role'],
                ':content' => $msg['content'],
                ':model'   => $this->model,
            ]);
        }

        return $history;
    }
}
?>
```

---

## Section 5 — Denial Appeal Letter Generator

### PHP Code — Appeal Letter

```php
<?php
function generateAppealLetter(
    string $apiKey,
    array  $userInfo,
    string $denialReason,
    string $programName
): string {
    $prompt = "Write a formal appeal letter for a denied $programName application.\n\n"
            . "Denial reason: $denialReason\n"
            . "Applicant name: {$userInfo['name']}\n"
            . "State: {$userInfo['state']}\n"
            . "Household size: {$userInfo['household_size']}\n"
            . "Annual income: \${$userInfo['annual_income']}\n\n"
            . "Write a professional, factual appeal letter that:\n"
            . "1. States the denial is being appealed\n"
            . "2. Provides factual basis for eligibility\n"
            . "3. Cites relevant federal poverty guidelines\n"
            . "4. Requests expedited review\n"
            . "5. Lists supporting documents being attached\n"
            . "Format as a proper business letter. Do not include placeholder brackets.";

    $payload = [
        'model'      => 'claude-sonnet-4-6',
        'max_tokens' => 1200,
        'system'     => 'You are a benefits legal advocate who writes formal appeal letters. '
                      . 'Write professional, factual letters that cite relevant regulations.',
        'messages'   => [['role' => 'user', 'content' => $prompt]]
    ];

    $ch = curl_init('https://api.anthropic.com/v1/messages');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => json_encode($payload),
        CURLOPT_HTTPHEADER     => [
            'Content-Type: application/json',
            "x-api-key: $apiKey",
            'anthropic-version: 2023-06-01',
        ],
    ]);
    $body = curl_exec($ch);
    curl_close($ch);

    $data = json_decode($body, true);
    return $data['content'][0]['text'] ?? '';
}
?>
```

---

## API Tags Reference

| Tag Name | Usage | Value Example |
|---|---|---|
| `anthropic-version` | Required header | `2023-06-01` |
| `x-api-key` | Auth header | `sk-ant-...` |
| `model` | Which model | `claude-sonnet-4-6` |
| `max_tokens` | Output limit | `1000` – `4000` |
| `system` | Behavior instructions | See prompts above |
| `messages[].role` | Turn type | `user` or `assistant` |
| `messages[].content` | Message text | String |
| `temperature` | Randomness (0-1) | `0.3` for factual |
| `stop_sequences` | Stop generation | `["</json>"]` |

### Response Tags

| Tag | Contains |
|---|---|
| `content[0].type` | Always `"text"` for standard requests |
| `content[0].text` | Claude's response text |
| `usage.input_tokens` | Tokens in prompt (billed) |
| `usage.output_tokens` | Tokens in response (billed) |
| `stop_reason` | Why generation stopped: `end_turn` or `max_tokens` |

---

## Cost Estimation (claude-sonnet-4-6)

| Operation | Est. Input Tokens | Est. Output Tokens | Est. Cost/Call |
|---|---|---|---|
| Eligibility check | 800 | 1,200 | ~$0.012 |
| Benefits explanation | 400 | 400 | ~$0.004 |
| Document guidance | 300 | 500 | ~$0.005 |
| Chat response | 1,500 | 300 | ~$0.011 |
| Appeal letter | 600 | 900 | ~$0.009 |

*Pricing: $3.00/MTok input, $15.00/MTok output (check docs.anthropic.com for current rates)*

---

## Error Handling

```php
<?php
function callClaudeWithRetry(array $payload, string $apiKey, int $maxRetries = 3): array {
    $retryable = [429, 500, 502, 503, 529]; // rate limit, overload
    
    for ($i = 0; $i < $maxRetries; $i++) {
        $ch = curl_init('https://api.anthropic.com/v1/messages');
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => json_encode($payload),
            CURLOPT_HTTPHEADER     => [
                'Content-Type: application/json',
                "x-api-key: $apiKey",
                'anthropic-version: 2023-06-01',
            ],
            CURLOPT_TIMEOUT => 60,
        ]);
        $body   = curl_exec($ch);
        $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($status === 200) {
            return json_decode($body, true);
        }
        
        if (in_array($status, $retryable)) {
            $delay = pow(2, $i); // exponential backoff: 1s, 2s, 4s
            sleep($delay);
            continue;
        }

        // Non-retryable error
        $err = json_decode($body, true);
        throw new RuntimeException(
            "Claude API error $status: " . ($err['error']['message'] ?? $body)
        );
    }

    throw new RuntimeException('Claude API failed after ' . $maxRetries . ' retries');
}
?>
```

---

## Environment Setup (.env)

```
ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
CLAUDE_MODEL=claude-sonnet-4-6
CLAUDE_MAX_TOKENS_ELIGIBILITY=4000
CLAUDE_MAX_TOKENS_CHAT=500
CLAUDE_MAX_TOKENS_LETTER=1200
CLAUDE_TEMPERATURE=0.3
```
