Install the Up2Data skill

Teach Claude Code to integrate the Up2Data API. Installs once, then loads automatically whenever you ask it about Up2Data.

  1. Run this in your terminal
    bash
    mkdir -p ~/.claude/skills/up2data-api && curl -fsSL https://up2data.ai/docs/v1/skill.md -o ~/.claude/skills/up2data-api/SKILL.md
  2. Restart Claude CodeSo it picks up the new skill.
  3. Try it"Enrich these LinkedIn profiles with Up2Data and send results to my webhook"

Requires Claude Code. macOS, Linux, or WSL.

Prefer to read it first? View the skill file

Webhooks

batch.completed delivery, signature verification, and the retry schedule.

#batch.completed

The only webhook event /v1 sends today. It fires once a POST /v1/batch job finishes, whether every item resolved or some failed.

http
POST https://your-server.com/hooks/up2data
X-Up2Data-Event: batch.completed
X-Up2Data-Signature: t=1767225600,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
Content-Type: application/json
json
{
  "event": "batch.completed",
  "job": {
    "job_id": "job_7hd92kfa",
    "status": "completed",
    "total": 1000,
    "estimated_completion": "2026-07-07T14:03:00Z"
  }
}

The payload is a summary, not the results - fetch those with GET /v1/batch/{job_id} or GET /v1/batch/{job_id}/results once you get the event.

Info

Return any 2xx status within 10 seconds to acknowledge delivery. Anything else counts as a failed attempt.

#Verifying the signature

X-Up2Data-Signature is an HMAC-SHA256 of {timestamp}.{raw_body}, computed with your webhook secret over the raw request body, before you parse it as JSON.

python
import hashlib, hmac, time

def verify(payload: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
    pairs = dict(p.split("=", 1) for p in header.split(","))
    timestamp, signature = pairs["t"], pairs["v1"]
    if abs(time.time() - int(timestamp)) > tolerance:
        return False  # replay protection
    expected = hmac.new(
        secret.encode(), f"{timestamp}.".encode() + payload, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)
javascript
import crypto from "node:crypto";

function verify(payload, header, secret, tolerance = 300) {
  const pairs = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const { t, v1 } = pairs;
  if (Math.abs(Date.now() / 1000 - Number(t)) > tolerance) return false;
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${t}.${payload}`)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}

#Retries

If your endpoint doesn't return a 2xx within 10 seconds, delivery is retried 5 times with exponential backoff: 1m, 5m, 30m, 2h, 6h. Retries carry the same job_id, so dedupe on it rather than assuming one delivery per job.

#Skipping webhooks entirely

webhook_url is optional on POST /v1/batch. Without it, poll GET /v1/batch/{job_id} instead - see Batch jobs.