Herringbone API guide

Welcome to the Herringbone API! This guide will help you integrate decision intelligence into your applications quickly and effectively.

Table of ContentsCopied!

Getting StartedCopied!

The Herringbone API provides two powerful capabilities:

  1. Decision Analysis - Get AI-powered advice on any decision

  2. Decision Extraction - Automatically find decisions in text

Base URL

https://herringbone.com/api/v1

Quick Setup

  1. Sign up at herringbone.com

  2. Navigate to API Keys in your dashboard

  3. Create a new API key

  4. Start making requests!

AuthenticationCopied!

Herringbone supports two authentication methods. Choose the one that works best for your application:

Bearer Token (Recommended)

curl -H "Authorization: Bearer YOUR_API_KEY" \
     https://herringbone.com/api/v1/decide

API Key Header

curl -H "X-API-Key: YOUR_API_KEY" \
     https://herringbone.com/api/v1/decide

Security Best Practices

  • Never expose API keys in client-side code

  • Use environment variables to store keys

  • Rotate keys regularly

  • Use different keys for development and production

Making Your First RequestCopied!

Let's start with a strategic decision:

curl -X POST https://herringbone.com/api/v1/decide \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Should we focus on customer acquisition or retention this quarter?",
    "model": "snap"
  }'

You'll receive a response in about 2 seconds:

{
  "requestId": "req_abc123",
  "model": "snap",
  "answer": "Focus on retention this quarter. It's 5-7x cheaper to retain existing customers than acquire new ones. With economic uncertainty, maximizing revenue from your current base while improving product-market fit will set you up for more efficient acquisition later.",
}

Understanding ModelsCopied!

Herringbone offers two models optimized for different scenarios:

Snap Model

  • Speed: 2 seconds

  • Best for: Tactical business decisions, time-sensitive choices

  • Examples: Pricing changes, feature prioritization, marketing campaigns

Advanced Model

  • Speed: 3-4 minutes

  • Best for: Strategic decisions, fundraising, major pivots

  • Features: Multiple perspectives, risk analysis, competitive landscape, long-term implications

Choosing the Right Model

// Quick tactical decision
const tacticalDecision = {
  message: "Should we launch our beta to the waitlist now or wait for mobile support?",
  model: "snap"
};

// Complex strategic decision
const strategicDecision = {
  message: "Should we pivot from B2C to B2B given our early enterprise interest?",
  model: "advanced",
  context: [
    {
      role: "user",
      content: "3 enterprise leads, struggling with consumer acquisition, 8 months runway"
    }
  ]
};

Common Use CasesCopied!

1. Startup Decision Engine

Integrate strategic decision-making into your product:

import requests

def get_business_advice(question):
    response = requests.post(
        "https://herringbone.com/api/v1/decide",
        headers={"Authorization": f"Bearer {API_KEY}"},
        json={"message": question, "model": "snap"}
    )
    return response.json()["answer"]

# Usage
advice = get_business_advice("Should we offer a freemium tier or free trial?")
print(advice)

2. Board Meeting Prep

Extract key decisions from investor emails:

async function processEmail(emailContent) {
  const response = await fetch('https://herringbone.com/api/v1/extract', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      text: emailContent,
      metadata: {
        source: 'email',
        from: '[email protected]'
      }
    })
  });
  
  const data = await response.json();
  return data.decisions;
}

3. Fundraising Strategy

Analyze complex funding decisions with full context:

def analyze_business_decision(question, context):
    response = requests.post(
        "https://herringbone.com/api/v1/decide",
        headers={"Authorization": f"Bearer {API_KEY}"},
        json={
            "message": question,
            "model": "advanced",
            "context": [{"role": "user", "content": context}]
        }
    )
    return response.json()

4. Product Roadmap Analysis

Extract strategic decisions from planning documents:

const extractMeetingDecisions = async (transcript) => {
  const response = await fetch('https://herringbone.com/api/v1/extract', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      text: transcript,
      metadata: { source: 'document' }
    })
  });
  
  const { decisions } = await response.json();
  
  // Group by type
  const stated = decisions.filter(d => d.type === 'stated');
  const implied = decisions.filter(d => d.type === 'implied');
  
  return { stated, implied };
};

Best PracticesCopied!

1. Provide Context

Better context leads to better decisions:

// Good - includes relevant context
{
  "message": "Should we expand to Europe or focus on the US market?",
  "context": [
    {
      "role": "user",
      "content": "$2M ARR in US, 30% of inbound leads from EU, Series A runway of 18 months"
    }
  ]
}

// Less effective - no context
{
  "message": "Should we expand internationally?"
}

2. Frame Clear Questions

Be specific about what you're deciding:

// Good - clear decision point
"Should we hire a VP of Sales now or wait until we hit $5M ARR?"

// Less effective - vague
"When should we scale our sales team?"

3. Batch Processing

For multiple decisions, process them efficiently:

async def process_decisions_batch(decisions):
    tasks = []
    for decision in decisions:
        task = make_decision_async(decision)
        tasks.append(task)
    
    results = await asyncio.gather(*tasks)
    return results

4. Error Handling

Always implement robust error handling:

try {
  const response = await fetch('https://herringbone.com/api/v1/decide', {
    method: 'POST',
    headers: headers,
    body: JSON.stringify(payload)
  });
  
  if (!response.ok) {
    const error = await response.json();
    console.error(`API Error: ${error.code} - ${error.error}`);
    // Handle specific error codes
    switch(response.status) {
      case 401:
        // Refresh API key or prompt for authentication
        break;
      case 429:
        // Implement exponential backoff
        break;
      default:
        // Generic error handling
    }
  }
  
  const data = await response.json();
  return data;
} catch (error) {
  console.error('Network error:', error);
  // Implement retry logic
}

Code ExamplesCopied!

Python Example

import requests
import json

API_KEY = "YOUR_API_KEY"
BASE_URL = "https://herringbone.com/api/v1"

# Quick tactical decision
response = requests.post(
    f"{BASE_URL}/decide",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "message": "Should we increase pricing before or after the product update?",
        "model": "snap"
    }
)
result = response.json()
print(result["answer"])

# Extract decisions from investor email
email_content = """
Thanks for the update. A few things we need to decide before next board meeting:
1. Should you raise Series A now or extend runway with venture debt?
2. Is it time to expand to enterprise or keep focusing on SMB?
Also wondering if you should bring on a CFO at this stage.
"""

response = requests.post(
    f"{BASE_URL}/extract",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "text": email_content,
        "metadata": {
            "source": "email",
            "from": "[email protected]"
        }
    }
)
data = response.json()

for decision in data["decisions"]:
    print(f"📌 {decision['question']} (Type: {decision['type']})")

Node.js Example

const API_KEY = process.env.HB_API_KEY;
const BASE_URL = 'https://herringbone.com/api/v1';

// Simple decision request
async function getAdvice(question, model = 'snap') {
  const response = await fetch(`${BASE_URL}/decide`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ message: question, model })
  });
  
  const data = await response.json();
  return data.answer;
}

// Decision extraction from document
async function extractDecisions(text) {
  const response = await fetch(`${BASE_URL}/extract`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      text,
      metadata: { source: 'document' }
    })
  });
  
  const data = await response.json();
  
  // Create action items
  const actionItems = data.decisions.map(d => ({
    question: d.question,
    type: d.type,
    confidence: d.confidence
  }));
  
  return actionItems;
}

React Integration

import { useState } from 'react';

function DecisionHelper() {
  const [question, setQuestion] = useState('');
  const [advice, setAdvice] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  const handleSubmit = async (e) => {
    e.preventDefault();
    setLoading(true);
    setError(null);
    
    try {
      const response = await fetch('https://herringbone.com/api/v1/decide', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.REACT_APP_HB_API_KEY}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          message: question,
          model: 'snap'
        })
      });
      
      if (!response.ok) {
        throw new Error('Request failed');
      }
      
      const data = await response.json();
      setAdvice(data.answer);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };
  
  return (
    <div>
      <form onSubmit={handleSubmit}>
        <input
          type="text"
          value={question}
          onChange={(e) => setQuestion(e.target.value)}
          placeholder="e.g., Should we launch on Product Hunt this week?"
        />
        <button type="submit" disabled={loading}>
          {loading ? 'Thinking...' : 'Get Advice'}
        </button>
      </form>
      
      {advice && (
        <div className="advice-card">
          <h3>Here's my recommendation:</h3>
          <p>{advice}</p>
        </div>
      )}
      
      {error && (
        <div className="error">
          Error: {error.message}
        </div>
      )}
    </div>
  );
}

Error HandlingCopied!

Common Error Codes

Code

Description

Solution

INVALID_REQUEST

Malformed request

Check request format and required fields

UNAUTHORIZED

Invalid API key

Verify API key is correct and active

RATE_LIMITED

Too many requests

Implement exponential backoff

INTERNAL_ERROR

Server error

Retry with exponential backoff

Retry Strategy

async function makeRequestWithRetry(payload, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(API_URL, {
        method: 'POST',
        headers: headers,
        body: JSON.stringify(payload)
      });
      
      if (response.status === 429) {
        // Rate limited - wait before retry
        const retryAfter = response.headers.get('Retry-After') || Math.pow(2, i);
        await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
        continue;
      }
      
      if (response.ok) {
        return await response.json();
      }
      
      throw new Error(`API error: ${response.status}`);
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000));
    }
  }
}

StreamingCopied!

Server-Sent Events (SSE)

For real-time updates during advanced analysis, enable streaming:

// Note: Native EventSource doesn't support custom headers
// Use a POST request with stream processing instead
async function streamDecision(question) {
  const response = await fetch('https://herringbone.com/api/v1/decide', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      message: question,
      model: 'advanced',
      stream: true
    })
  });
  
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    
    const chunk = decoder.decode(value);
    const lines = chunk.split('\n');
    
    for (const line of lines) {
      if (line.startsWith('data: ')) {
        const data = JSON.parse(line.slice(6));
        
        switch(data.type) {
          case 'start':
            console.log('Analysis started...');
            break;
          case 'progress':
            console.log(`Progress: ${data.message}`);
            break;
          case 'complete':
            console.log('Analysis complete:', data.answer);
            return data.answer;
        }
      }
    }
  }
}

Building Your Own IntegrationCopied!

The Herringbone API is straightforward to integrate into any language or framework. Here's an example implementation:

class HerringboneClient:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://herringbone.com/api/v1"
        
    def _make_request(self, endpoint, payload):
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }
        response = requests.post(
            f"{self.base_url}/{endpoint}",
            headers=headers,
            json=payload
        )
        response.raise_for_status()
        return response.json()
    
    def decide(self, message, model="snap", context=None, stream=False):
        payload = {
            "message": message,
            "model": model,
            "stream": stream
        }
        if context:
            payload["context"] = context
        return self._make_request("decide", payload)
    
    def extract(self, text, metadata=None):
        payload = {"text": text}
        if metadata:
            payload["metadata"] = metadata
        return self._make_request("extract", payload)

SupportCopied!

Ready to add decision intelligence to your app? Get your API key and start building!