Vai al contenuto principale
Integrazione ATS

Invia candidati verificati nel tuo ATS esistente

InTransparency non sostituisce il tuo ATS: lo alimenta. Quando scatta un match ad alta affidabilità, inviamo un webhook firmato; un relay di 30 righe lo trasforma in un prospect Greenhouse, un'opportunity Lever o un candidate di requisition Workday.

Lo schema

InTransparency invia un webhook → il tuo piccolo relay verifica la firma → il relay chiama l'endpoint candidates del tuo ATS. Nessun middleware custom, nessun polling, nessuna sincronizzazione pianificata. Il relay gira ovunque (Vercel, Cloudflare Workers, AWS Lambda o un container FastAPI).

1. Crea una chiave agent e registra un webhook

# Step 1: Create an InTransparency agent key with webhook scope
curl -X POST https://www.in-transparency.com/api/agents/keys \
  -H "Cookie: <your session>" \
  -H "Content-Type: application/json" \
  -d '{"name":"ATS-relay","scopes":["agent:webhook"]}'
# Response: { "key": "ita_...", ... } — save it once; never shown again

# Step 2: Register your webhook endpoint with InTransparency
curl -X POST https://www.in-transparency.com/api/agents/webhooks \
  -H "Authorization: Bearer ita_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-relay.example.com/webhooks/intransparency",
    "events": ["match.created", "credential.issued"]
  }'
# Response includes { "secret": "..." } — store as INTRANSPARENCY_WEBHOOK_SECRET

2. Relay Greenhouse (Node / TypeScript)

Greenhouse Harvest API

// Deno / Node webhook relay: InTransparency → Greenhouse Harvest API
// Env: GREENHOUSE_API_KEY, INTRANSPARENCY_WEBHOOK_SECRET, GREENHOUSE_JOB_ID
import crypto from 'node:crypto'

export default async function relay(req: Request): Promise<Response> {
  // 1. Verify the signature
  const body = await req.text()
  const sig = req.headers.get('X-InTransparency-Signature') || ''
  const expected = 'sha256=' + crypto
    .createHmac('sha256', process.env.INTRANSPARENCY_WEBHOOK_SECRET!)
    .update(body).digest('hex')
  if (sig !== expected) return new Response('bad signature', { status: 401 })

  const { event, data } = JSON.parse(body)
  if (event !== 'match.created' || data.matchScore < 70) {
    return new Response('ignored', { status: 200 })
  }

  // 2. Fetch the candidate's full profile from InTransparency
  const candidate = await fetch(
    'https://www.in-transparency.com/api/agents/companies/' + data.candidateSlug,
  ).then(r => r.json())

  // 3. Create a prospect in Greenhouse — the "Prospects" stage is perfect
  // for sourced leads; they convert to candidates once a recruiter adds them
  // to a job.
  await fetch('https://harvest.greenhouse.io/v1/candidates', {
    method: 'POST',
    headers: {
      'Authorization': 'Basic ' + Buffer.from(process.env.GREENHOUSE_API_KEY + ':').toString('base64'),
      'Content-Type': 'application/json',
      'On-Behalf-Of': process.env.GREENHOUSE_USER_ID!,
    },
    body: JSON.stringify({
      first_name: data.candidateName.split(' ')[0],
      last_name: data.candidateName.split(' ').slice(1).join(' '),
      email_addresses: [{ value: candidate.email, type: 'personal' }],
      applications: [{ job_id: Number(process.env.GREENHOUSE_JOB_ID) }],
      attachments: [{
        filename: 'intransparency-evidence.pdf',
        type: 'other',
        url: 'https://www.in-transparency.com/en/matches/' + data.matchId + '/why',
      }],
      custom_fields: {
        intransparency_match_score: data.matchScore,
        intransparency_match_id: data.matchId,
      },
    }),
  })

  return new Response('ok', { status: 200 })
}

3. Relay Lever (Python / FastAPI)

Lever Opportunities API

# Python / FastAPI webhook relay: InTransparency → Lever API
# Env: LEVER_API_KEY, INTRANSPARENCY_WEBHOOK_SECRET, LEVER_POSTING_ID
import hashlib, hmac, os, json, httpx
from fastapi import FastAPI, Request, HTTPException

app = FastAPI()

@app.post("/webhooks/intransparency")
async def relay(req: Request):
    body = await req.body()
    sig = req.headers.get("X-InTransparency-Signature", "")
    expected = "sha256=" + hmac.new(
        os.environ["INTRANSPARENCY_WEBHOOK_SECRET"].encode(),
        body, hashlib.sha256,
    ).hexdigest()
    if not hmac.compare_digest(sig, expected):
        raise HTTPException(401, "bad signature")

    payload = json.loads(body)
    if payload["event"] != "match.created": return {"ok": True}
    data = payload["data"]
    if data["matchScore"] < 70: return {"ok": True}

    # Lever: create an opportunity (candidate attached to a posting)
    async with httpx.AsyncClient(auth=(os.environ["LEVER_API_KEY"], "")) as client:
        r = await client.post(
            "https://api.lever.co/v1/opportunities",
            json={
                "name": data["candidateName"],
                "origin": "sourced",
                "sources": ["InTransparency"],
                "postings": [os.environ["LEVER_POSTING_ID"]],
                "tags": [f"intransparency-score-{data['matchScore']}"],
                "links": [data["url"]],
            },
        )
        r.raise_for_status()
    return {"ok": True}

Altre piattaforme ATS

Workday Recruiting

Usa il Recruiting Staffing Web Service (SOAP): aggiungi un candidato con l'operazione Add_Candidate. Stesso schema di relay, payload diverso.

SmartRecruiters

POST a /candidates con jobId e status=LEAD. I campi personalizzati si mappano facilmente.

Recruitee

POST a /candidates con placements=[{offer_id}]. Allega l'URL delle evidenze come nota.

Teamtailor

POST a /v1/candidates con job-applications incluse. I candidati sourced appaiono in pipeline come lead.

Personio

Usa la Recruiting API: POST /v1/recruiting/applications. I team HR italiani che già usano Personio possono collegarlo in meno di un'ora.

Ashby

POST a /candidate.create, poi /candidate.addApplication. La loro API è nativa webhook, quindi logica di relay minima.

    ATS integration — Greenhouse, Lever, Workday | InTransparency | InTransparency