---
name: juicebox-upgrade-migration
description: 'Plan Juicebox SDK upgrades.

  Trigger: "upgrade juicebox", "juicebox migration".

  '
allowed-tools: Read, Write, Edit, Bash(npm:*)
version: 1.0.0
license: MIT
author: Jeremy Longshore <jeremy@intentsolutions.io>
tags:
- saas
- recruiting
- juicebox
compatibility: Designed for Claude Code
---
# Juicebox Upgrade & Migration

## Overview

Juicebox is an AI-powered people search and analysis platform used for recruiting and market research. The API provides endpoints for dataset management, people searches, and AI-generated analyses. Tracking API versions is essential because Juicebox evolves its search query syntax, dataset schema, and analysis output format — upgrading without testing can break saved search filters, corrupt dataset imports, and change the structure of AI-generated candidate profiles that downstream systems consume.

## Version Detection

```typescript
const JUICEBOX_BASE = "https://api.juicebox.work/v1";

async function detectJuiceboxVersion(apiKey: string): Promise<void> {
  const res = await fetch(`${JUICEBOX_BASE}/datasets`, {
    headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
  });
  const version = res.headers.get("x-juicebox-api-version") ?? "v1";
  console.log(`Juicebox API version: ${version}`);

  // Check for deprecated search parameters
  const searchRes = await fetch(`${JUICEBOX_BASE}/search`, {
    method: "POST",
    headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
    body: JSON.stringify({ query: "test", limit: 1 }),
  });
  const deprecation = searchRes.headers.get("x-deprecated-params");
  if (deprecation) console.warn(`Deprecated search params: ${deprecation}`);
}
```

## Migration Checklist

- [ ] Review Juicebox changelog for API breaking changes
- [ ] Audit codebase for hardcoded dataset field names
- [ ] Verify search query syntax — filter operators may have changed
- [ ] Check analysis output format for new or renamed fields
- [ ] Update dataset import schema if column mapping changed
- [ ] Test people search result structure (profile fields, enrichment data)
- [ ] Validate pagination — cursor-based vs. offset may have changed
- [ ] Update SDK version in `package.json` and verify type compatibility
- [ ] Check webhook payloads for analysis completion events
- [ ] Run integration tests with sample dataset to verify search quality

## Schema Migration

```typescript
// Juicebox search results evolved: flat profile → enriched profile with sources
interface OldSearchResult {
  id: string;
  name: string;
  title: string;
  company: string;
  email?: string;
  linkedin_url?: string;
}

interface NewSearchResult {
  id: string;
  profile: {
    full_name: string;
    current_title: string;
    current_company: { name: string; domain: string };
    emails: Array<{ address: string; type: "work" | "personal"; verified: boolean }>;
    social: { linkedin?: string; twitter?: string };
  };
  match_score: number;
  enrichment_sources: string[];
}

function migrateSearchResult(old: OldSearchResult): NewSearchResult {
  return {
    id: old.id,
    profile: {
      full_name: old.name,
      current_title: old.title,
      current_company: { name: old.company, domain: "" },
      emails: old.email ? [{ address: old.email, type: "work", verified: false }] : [],
      social: { linkedin: old.linkedin_url },
    },
    match_score: 0,
    enrichment_sources: [],
  };
}
```

## Rollback Strategy

```typescript
class JuiceboxClient {
  private currentVersion: "v1" | "v2";

  constructor(private apiKey: string, version: "v1" | "v2" = "v2") {
    this.currentVersion = version;
  }

  async search(query: string, filters?: Record<string, any>): Promise<any> {
    try {
      const res = await fetch(`https://api.juicebox.work/${this.currentVersion}/search`, {
        method: "POST",
        headers: { Authorization: `Bearer ${this.apiKey}`, "Content-Type": "application/json" },
        body: JSON.stringify({ query, filters }),
      });
      if (!res.ok) throw new Error(`Juicebox search ${res.status}`);
      return await res.json();
    } catch (err) {
      if (this.currentVersion === "v2") {
        console.warn("Falling back to Juicebox API v1");
        this.currentVersion = "v1";
        return this.search(query, filters);
      }
      throw err;
    }
  }
}
```

## Error Handling

| Migration Issue | Symptom | Fix |
|----------------|---------|-----|
| Search filter syntax changed | `400 Bad Request` with `invalid filter operator` | Update filter syntax to new query DSL format |
| Dataset schema mismatch | Import succeeds but columns mapped incorrectly | Re-map dataset columns using `/datasets/schema` endpoint |
| Profile field restructured | Code crashes accessing `result.name` (now `result.profile.full_name`) | Update all property access paths to new nested structure |
| Analysis format changed | AI analysis output missing expected sections | Update parser for new structured analysis response |
| Rate limit reduced | `429 Too Many Requests` on previously working batch sizes | Reduce batch size and implement request queuing |

## Resources

- Juicebox Changelog
- Juicebox API Documentation

## Next Steps

For CI pipeline integration, see `juicebox-ci-integration`.
