Skip to main content

Overview

This guide covers the full partner integration lifecycle: authenticating, configuring signals, consuming the feed, enriching leads with deep research, and receiving events via webhooks.

Prerequisites

  • A Puffle partner account
  • Your API key (pk_live_...) from Settings → API
  • An HTTPS endpoint for webhooks (optional but recommended)

1. Authenticate

Set your API key as an environment variable and verify access:
export PUFFLE_API_KEY="pk_live_abc123"

curl -s \
  -H "Authorization: Bearer $PUFFLE_API_KEY" \
  "https://app.getlima.ai/api/v1/signals/types"
A 200 response with signal types confirms authentication is working.

2. Configure Signal Types

Enable built-in types

All built-in types are enabled by default. Tune them to your ICP:
# Enable and tune the Hiring signal
curl -s -X PATCH \
  -H "Authorization: Bearer $PUFFLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "keywords": ["VP Engineering", "Head of Engineering", "CTO", "engineering leader"],
    "scoring_criteria": "Prioritize senior technical leadership roles at B2B SaaS companies 50-500 employees"
  }' \
  "https://app.getlima.ai/api/v1/signals/types/HIRING_TYPE_ID"

Create custom signal types

curl -s -X POST \
  -H "Authorization: Bearer $PUFFLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Competitor Mentions",
    "description": "Track when prospects mention competitors or express dissatisfaction",
    "keywords": ["switching from", "alternatives to", "compared to", "frustrated with"],
    "search_instructions": "Find posts or discussions where people express intent to switch tools or compare competitors in our space.",
    "scoring_criteria": "Score highest for direct comparison requests or explicit dissatisfaction. Score lower for generic mentions.",
    "sources": ["reddit", "linkedin_posts", "hacker_news"],
    "enabled": true
  }' \
  "https://app.getlima.ai/api/v1/signals/types"

3. Set Up Webhooks

Configure webhooks to receive signals in real time instead of polling:
# Get the Svix portal URL
PORTAL_URL=$(curl -s \
  -H "Authorization: Bearer $PUFFLE_API_KEY" \
  "https://app.getlima.ai/api/v1/partners/portal" | jq -r '.url')

echo "Open in browser: $PORTAL_URL"
In the Svix portal:
  1. Add your HTTPS endpoint URL
  2. Subscribe to signal.detected and research.complete
  3. Copy the signing secret for verification

4. Receive and Verify Webhook Events

from flask import Flask, request, abort
from svix.webhooks import Webhook, WebhookVerificationError

app = Flask(__name__)
WEBHOOK_SECRET = "whsec_..."  # from Svix portal

@app.route("/webhooks/puffle", methods=["POST"])
def handle_puffle_webhook():
    wh = Webhook(WEBHOOK_SECRET)
    try:
        payload = wh.verify(request.data, dict(request.headers))
    except WebhookVerificationError:
        abort(400)

    event_type = payload["type"]

    if event_type == "signal.detected":
        handle_signal(payload["data"])
    elif event_type == "research.complete":
        handle_research(payload["data"])

    return "", 200

def handle_signal(signal: dict):
    print(f"New signal: [{signal['score']}] {signal['headline']}")
    # Push to CRM, trigger outreach, etc.

def handle_research(research: dict):
    print(f"Research complete: {research['first_name']} {research['last_name']} (ICP: {research['icp_score']})")
    # Attach report to CRM record, trigger sales sequence, etc.

5. Trigger Deep Research on High-Score Signals

When a high-scoring signal fires on a company, automatically research the relevant contact:
def handle_signal(signal: dict):
    if signal["score"] >= 80:
        # Trigger deep research on the relevant contact
        resp = requests.post(
            "https://app.getlima.ai/api/v1/deep-research",
            headers={"Authorization": f"Bearer {PUFFLE_API_KEY}", "Content-Type": "application/json"},
            json={
                "first_name": signal["metadata"].get("contact_first_name"),
                "last_name": signal["metadata"].get("contact_last_name"),
                "company": signal["metadata"].get("company"),
                "title": signal["metadata"].get("title"),
            }
        )
        research = resp.json()
        print(f"Research submitted: {research['id']}")

6. Full Integration Checklist

1

Authentication verified

GET /signals/types returns 200 with your configured types.
2

Signal types configured

Built-in types tuned to your ICP. Custom types created for your use case.
3

Webhook endpoint live

HTTPS endpoint accepting POST requests, verifying Svix signatures, returning 200.
4

Signal routing implemented

Signals routed to CRM or outreach system based on type, score, and source.
5

Deep research triggered

High-score signals automatically trigger deep research on the relevant contact.
6

Feedback loop active

Irrelevant signals marked via POST /signals/:id/feedback to improve scoring.

Rate Limits & Scaling

  • 100 req/min per API key — sufficient for most polling-based integrations
  • For high-volume use cases, prefer webhooks to eliminate polling overhead
  • Batch paginate with limit=100 to minimize request count when backfilling
See Rate Limits for retry guidance.