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://docs.up2data.ai/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.

A signal sends the same event once per run, to the webhook_url in its request. Map the job_id back to the signal with GET /v1/signals/{signal_id}/runs, or keep one webhook URL per signal.

Info

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

#Your signing secret

Every organization has one signing secret, a whsec_ string that is the same for every webhook you receive. Read it from webhook.signing_secret on GET /v1/account, or copy it from Settings, Webhook Integrations in the portal; it is created the first time it is read or used. To replace it, call POST /v1/account/webhook-secret/rotate or press Rotate in the portal. Rotation is immediate and there is no overlap window: deliveries after that point, including retries of earlier jobs, are signed with the new secret only, so switch your verifier first.

#Verifying the signature

X-Up2Data-Signature is an HMAC-SHA256 of {timestamp}.{raw_body}, computed with your signing 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.