Skip to content
Back to skills

Juicebox Sdk Patterns

ASecurity

'Apply production Juicebox SDK patterns.

  • 2,719 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 21, 2026
ai-agentstypescriptpythontestingapi

Works with

  • claude code
  • cursor
  • cli
  • api

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned September 21, 2026

npx -y skills add jeremylongshore/claude-code-plugins-plus --skill juicebox-sdk-patterns --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Juicebox Sdk Patterns?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Juicebox Sdk Patterns
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/jeremylongshore-juicebox-sdk-patterns-0d42bfd5/badge)](https://www.skillsdirectory.com/skills/jeremylongshore-juicebox-sdk-patterns-0d42bfd5)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: juicebox-sdk-patterns
description: 'Apply production Juicebox SDK patterns.

  Trigger: "juicebox patterns", "juicebox best practices".

  '
allowed-tools: Read, Write, Edit
version: 1.16.0
license: MIT
author: Jeremy Longshore <jeremy@intentsolutions.io>
tags:
- saas
- recruiting
- juicebox
compatibility: Designed for Claude Code
---
# Juicebox SDK Patterns

## Overview

Production-ready patterns for the Juicebox AI-powered people search API. Juicebox provides REST endpoints for searching professional profiles and enriching candidate data. The API authenticates via `JUICEBOX_API_KEY` and returns structured profile objects with LinkedIn URLs as natural dedup keys. A singleton client centralizes rate-limit handling across search and enrich endpoints.

## Singleton Client

```typescript
const JUICEBOX_BASE = 'https://api.juicebox.work/v1';
let _client: JuiceboxClient | null = null;
export function getClient(): JuiceboxClient {
  if (!_client) {
    const apiKey = process.env.JUICEBOX_API_KEY;
    if (!apiKey) throw new Error('JUICEBOX_API_KEY must be set — get it from juicebox.work/settings');
    _client = new JuiceboxClient(apiKey);
  }
  return _client;
}
class JuiceboxClient {
  private headers: Record<string, string>;
  constructor(apiKey: string) { this.headers = { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }; }
  async search(query: string, limit = 20): Promise<SearchResponse> {
    const res = await fetch(`${JUICEBOX_BASE}/search`, {
      method: 'POST', headers: this.headers, body: JSON.stringify({ query, limit }) });
    if (!res.ok) throw new JuiceboxError(res.status, await res.text()); return res.json();
  }
  async enrich(linkedinUrl: string): Promise<Profile> {
    const res = await fetch(`${JUICEBOX_BASE}/enrich`, {
      method: 'POST', headers: this.headers, body: JSON.stringify({ linkedin_url: linkedinUrl }) });
    if (!res.ok) throw new JuiceboxError(res.status, await res.text()); return res.json();
  }
}
```

## Error Wrapper

```typescript
export class JuiceboxError extends Error {
  constructor(public status: number, message: string) { super(message); this.name = 'JuiceboxError'; }
}
export async function safeCall<T>(operation: string, fn: () => Promise<T>): Promise<T> {
  try { return await fn(); }
  catch (err: any) {
    if (err instanceof JuiceboxError && err.status === 429) { await new Promise(r => setTimeout(r, 5000)); return fn(); }
    if (err instanceof JuiceboxError && err.status === 401) throw new JuiceboxError(401, 'Invalid JUICEBOX_API_KEY');
    throw new JuiceboxError(err.status ?? 0, `${operation} failed: ${err.message}`);
  }
}
```

## Request Builder

```typescript
class JuiceboxSearchBuilder {
  private body: Record<string, any> = {};
  query(q: string) { this.body.query = q; return this; }
  limit(n: number) { this.body.limit = Math.min(n, 100); return this; }
  location(loc: string) { this.body.location = loc; return this; }
  title(t: string) { this.body.title_filter = t; return this; }
  company(c: string) { this.body.company_filter = c; return this; }
  yearsExp(min: number, max: number) { this.body.years_experience = { min, max }; return this; }
  build() { return this.body; }
}
// Usage: new JuiceboxSearchBuilder().query('ML engineer').location('San Francisco').yearsExp(3, 8).build();
```

## Response Types

```typescript
interface Profile {
  id: string; name: string; title: string; company: string;
  linkedin_url: string; location: string; skills: string[]; experience_years: number;
}
interface SearchResponse {
  profiles: Profile[]; total: number; has_more: boolean; cursor?: string;
}
interface EnrichResult {
  profile: Profile; education: Array<{ school: string; degree: string; year: number }>;
  experience: Array<{ company: string; title: string; start: string; end: string | null }>;
}
```

## Testing Utilities

```typescript
export function mockProfile(overrides: Partial<Profile> = {}): Profile {
  return { id: 'prof-001', name: 'Jane Smith', title: 'Senior ML Engineer',
    company: 'Acme Corp', linkedin_url: 'https://linkedin.com/in/janesmith',
    location: 'San Francisco, CA', skills: ['Python', 'PyTorch', 'MLOps'],
    experience_years: 6, ...overrides };
}
export function mockSearchResponse(count = 3): SearchResponse {
  return { profiles: Array.from({ length: count }, (_, i) => mockProfile({ id: `prof-${i}` })),
    total: count, has_more: false };
}
```

## Error Handling

| Pattern | When to Use | Example |
|---------|-------------|---------|
| `safeCall` wrapper | All Juicebox API calls | Structured error with operation context |
| Retry on 429 | Batch search pipelines | 5s backoff before retry |
| LinkedIn dedup | Multi-query search | `Set<string>` on `linkedin_url` prevents duplicates |
| Cursor pagination | Search results > 100 | Pass `cursor` from previous response |

## Prerequisites

- A typed client boundary, secret-manager reference, sandbox workspace, approved source/destination allowlist, and synthetic prospect fixture.

## Instructions

1. Construct clients from scoped environment configuration and reject unknown workspace, source, or destination before a request is sent.
2. Centralize source-authority, suppression, schema, redaction, and idempotency checks in the client boundary.
3. Model partial/denied responses explicitly; never turn a denied or unverified enrichment into a success or export fallback.
4. Test malformed data, rate limits, source/suppression rejection, and rollback using synthetic fixtures only.

## Output

Produce a client-contract receipt with SDK revision, environment, source/destination policy revisions, fixture result, error classification, aggregate counts, export-count assertion, and rollback behavior. Exclude contacts, enrichment data, and credentials.

## Examples

`sdk=v3; env=sandbox; source=synthetic; destination=approved; suppression=pass; malformed=blocked; contacts_exported=0; telemetry=aggregate-only` is a safe SDK result.

## Resources

- Juicebox API Docs

## Next Steps

Apply patterns in `juicebox-core-workflow-a`.

Files in this skill

  • SKILL.md5.9 KB
  • references/advanced-patterns.md7.2 KB
  • references/implementation-guide.md3.1 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.