Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Plan Juicebox SDK upgrades. Trigger: "upgrade juicebox", "juicebox migration".
.claude/skills/jeremylongshore-juicebox-upgrade-migration/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | -1% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 236% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 30% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 25% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 39% | 0% |
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.
typescriptconst 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}`); }
package.json and verify type compatibilitytypescript// 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: [], }; }
typescriptclass 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; } } }
| 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 |
For CI pipeline integration, see juicebox-ci-integration.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 25,054 | 18,727 | -25% | 1 | 1 | 0% | 4,214 | 4,151 | -1% | 0 | 0 | — |
case-02 | fail→pass | 31,269 | 18,020 | -42% | 1 | 1 | 0% | 1,593 | 5,353 | +236% | 0 | 0 | — |
case-03 | fail→pass | 19,130 | 22,274 | +16% | 1 | 1 | 0% | 3,592 | 4,663 | +30% | 0 | 0 | — |
case-04 | fail→pass | 12,940 | 16,244 | +26% | 1 | 1 | 0% | 2,878 | 3,610 | +25% | 0 | 0 | — |
case-09 | pass→pass | 7,950 | 3,667 | -54% | 1 | 1 | 0% | 1,334 | 1,878 | +41% | 0 | 0 | — |
case-05 | fail→pass | 16,490 | 11,109 | -33% | 1 | 1 | 0% | 1,660 | 2,301 | +39% | 0 | 0 | — |
case-06 | fail→pass | 16,746 | 2,698 | -84% | 1 | 1 | 0% | 1,961 | 1,694 | -14% | 0 | 0 | — |
case-07 | fail→pass | 16,340 | 6,953 | -57% | 1 | 1 | 0% | 1,892 | 1,611 | -15% | 0 | 0 | — |
case-08 | fail→pass | 16,980 | 3,444 | -80% | 1 | 1 | 0% | 2,022 | 1,779 | -12% | 0 | 0 | — |
case-22 | pass→pass | 21,304 | 20,358 | -4% | 1 | 1 | 0% | 3,283 | 3,706 | +13% | 0 | 0 | — |
case-10 | fail→pass | 18,247 | 3,578 | -80% | 1 | 1 | 0% | 1,891 | 1,851 | -2% | 0 | 0 | — |
case-11 | fail→pass | 16,682 | 9,061 | -46% | 1 | 1 | 0% | 1,888 | 1,843 | -2% | 0 | 0 | — |
case-12 | pass→pass | 16,040 | 3,655 | -77% | 1 | 1 | 0% | 1,727 | 1,796 | +4% | 0 | 0 | — |
case-13 | fail→pass | 15,065 | 3,280 | -78% | 1 | 1 | 0% | 1,537 | 1,892 | +23% | 0 | 0 | — |
case-14 | fail→pass | 10,036 | 2,960 | -71% | 1 | 1 | 0% | 1,579 | 1,672 | +6% | 0 | 0 | — |
case-15 | fail→pass | 20,048 | 14,510 | -28% | 1 | 1 | 0% | 2,304 | 3,156 | +37% | 0 | 0 | — |
case-16 | fail→fail | 20,241 | 16,183 | -20% | 1 | 1 | 0% | 2,106 | 2,878 | +37% | 0 | 0 | — |
case-17 | pass→pass | 10,963 | 10,747 | -2% | 1 | 1 | 0% | 1,742 | 2,229 | +28% | 0 | 0 | — |
case-18 | pass→pass | 13,407 | 8,508 | -37% | 1 | 1 | 0% | 2,182 | 2,709 | +24% | 0 | 0 | — |
case-19 | pass→pass | 17,639 | 9,813 | -44% | 1 | 1 | 0% | 2,194 | 2,998 | +37% | 0 | 0 | — |
case-20 | pass→pass | 15,595 | 16,954 | +9% | 1 | 1 | 0% | 2,548 | 4,469 | +75% | 0 | 0 | — |
case-21 | fail→pass | 16,596 | 9,082 | -45% | 1 | 1 | 0% | 1,787 | 2,684 | +50% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 21 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +64 percentage points is the difference between those two pass rates over the 21 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.