Feed verified candidates into your existing ATS
InTransparency doesn't replace your ATS — it feeds it. When a high-confidence match fires, we send a signed webhook; a 30-line relay turns that into a Greenhouse prospect, a Lever opportunity, or a Workday requisition candidate.
The pattern
InTransparency fires a webhook → your tiny relay verifies the signature → your relay calls your ATS's candidates endpoint. No custom middleware, no polling, no scheduled sync. The relay runs anywhere (Vercel, Cloudflare Workers, AWS Lambda, or a FastAPI container).
1. Create an agent key and register a 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_SECRET2. Greenhouse relay (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. Lever relay (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}Other ATS platforms
Workday Recruiting
Use the Recruiting Staffing Web Service (SOAP) — add a Candidate with Add_Candidate operation. Same relay pattern, different payload.
SmartRecruiters
POST to /candidates with jobId and status=LEAD. Custom fields map cleanly.
Recruitee
POST to /candidates with placements=[{offer_id}]. Attach evidence URL as note.
Teamtailor
POST to /v1/candidates with included job-applications. Sourced candidates appear in the pipeline as leads.
Personio
Use the Recruiting API — POST /v1/recruiting/applications. Italian HR teams already familiar with Personio can wire this up in under an hour.
Ashby
POST to /candidate.create, then /candidate.addApplication. Their API is webhook-native, so minimal relay logic.