Talvin API Documentation
Read your interviews and candidate responses, and subscribe to webhooks for real-time events — connect Talvin to your ATS, internal dashboards, or any custom tool.
1 · Getting your credentials
You need two values, both in the Talvin dashboard under Settings → Integrations:
| Credential | Where to find it | Example |
|---|---|---|
| Organization ID | The “Organization ID” card at the top of the Integrations page | org_2abc… |
| API Key | API Keys tab → Create API Key → choose Other (or a platform) | talvin_a1b2c3… |
2 · Authentication
Pass both credentials as headers on every request:
X-TALVIN-API-KEY: talvin_your_api_key
X-TALVIN-ORGANIZATION-ID: org_your_organization_id
Quick test — verify your credentials:
curl https://app.talvin.ai/api/v1/whoami \
-H "X-TALVIN-API-KEY: talvin_your_api_key" \
-H "X-TALVIN-ORGANIZATION-ID: org_your_organization_id"
{ "success": true, "data": { "id": "0f4c1…", "name": "Other" } }
A 401 means the key or organization ID is missing or invalid.
3 · Rate limits
Requests are limited to 100 requests per 5 seconds per organization. Every response includes:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests allowed in the current window |
X-RateLimit-Remaining | Requests remaining in the current window |
When exceeded, the API returns 429 with { "error": "Rate limit exceeded" } — back off briefly and retry.
4 · Endpoints
/whoamiValidates your credentials. Returns the API key's id and name.
{ "success": true, "data": { "id": "0f4c1…", "name": "Other" } }
/interviewsLists all interviews in your organization, newest first.
| Field | Type | Description |
|---|---|---|
id | string | Interview ID |
name | string | Interview / role name |
description | string | Interview description |
question_count | number | Number of main questions |
time_duration | string | Planned duration in minutes |
questions | array | [{ id, question, follow_up_count }] |
is_active | boolean | Whether the interview currently accepts candidates |
[
{
"id": "GCV-5rn07_oOpQwsweQTv",
"name": "Client Relationship Officer",
"description": "Screening for the CRO voice-process role",
"question_count": 5,
"time_duration": "10",
"questions": [
{ "id": "q1", "question": "Tell me about yourself", "follow_up_count": 1 }
],
"is_active": true
}
]
/interviews/{id}Fetches a single interview by ID. Same fields as above (without is_active). Returns 404 if the interview doesn't exist or belongs to another organization.
/responsesLists the 100 most recent candidate responses across all your interviews, newest first.
| Field | Type | Description |
|---|---|---|
id | number | Response ID |
created_at | string | ISO 8601 — when the candidate started |
name | string | Candidate name |
email | string | Candidate email |
call_id | string | Voice-call ID |
duration | number | Call duration in seconds |
candidate_status | string | NO_STATUS · POTENTIAL · SELECTED · NOT_SELECTED |
interview | object | { id, name, organization_id } |
[
{
"id": 1014,
"created_at": "2026-07-08T15:13:30.841Z",
"name": "Vishnu Reddy",
"email": "candidate@example.com",
"call_id": "call_345249d369b0…",
"duration": 189,
"candidate_status": "NO_STATUS",
"interview": {
"id": "GCV-5rn07_oOpQwsweQTv",
"name": "Client Relationship Officer",
"organization_id": "org_3Fo3T4FzHW…"
}
}
]
5 · Webhooks
Instead of polling, subscribe a URL to receive events as JSON POST requests the moment they happen.
Events
| eventType | Fired when | Payload |
|---|---|---|
new_interview | A new interview is created in your organization | The interview object (id, name, question_count, time_duration, questions, description) |
candidate_status_changed | A candidate's status is updated in the dashboard (e.g. marked Selected) | The full response object (same shape as GET /responses items) |
new_response | — | Reserved for future use; not currently emitted |
/subscribecurl -X POST https://app.talvin.ai/api/v1/subscribe \
-H "X-TALVIN-API-KEY: $TALVIN_API_KEY" \
-H "X-TALVIN-ORGANIZATION-ID: $TALVIN_ORG_ID" \
-H "Content-Type: application/json" \
-d '{
"hookUrl": "https://your-app.example.com/webhooks/talvin",
"eventType": "candidate_status_changed"
}'
Response 201:
{ "id": "3d5a7e12-…" }
Store this subscription id — you need it to unsubscribe. Subscribe once per event type if you want multiple events.
/unsubscribe/{subscriptionId}curl -X DELETE https://app.talvin.ai/api/v1/unsubscribe/3d5a7e12-… \
-H "X-TALVIN-API-KEY: $TALVIN_API_KEY" \
-H "X-TALVIN-ORGANIZATION-ID: $TALVIN_ORG_ID"
Returns 204 No Content on success.
Delivery semantics — please read
- Events are delivered as
POSTwithContent-Type: application/jsonand a 5-second timeout. Respond with200quickly; do heavy processing asynchronously. - Delivery is at-most-once — there are currently no automatic retries. If your endpoint is down when an event fires, that delivery is missed; use
GET /responsesto reconcile. - Respond
410 Goneif you want Talvin to permanently deactivate the subscription. - Repeatedly failing endpoints may be automatically deactivated. If events stop arriving, re-subscribe.
6 · Errors
Errors return a JSON body { "error": "message" } with standard status codes:
| Status | Meaning |
|---|---|
400 | Malformed request (e.g. missing hookUrl) |
401 | Missing or invalid API key / organization ID |
404 | Resource not found (or belongs to another organization) |
429 | Rate limit exceeded — retry after a short delay |
500 | Something went wrong on our side — safe to retry; contact support if persistent |
7 · Recipes
Sync candidates into your system every 5 minutes (polling):
curl -s https://app.talvin.ai/api/v1/responses \
-H "X-TALVIN-API-KEY: $TALVIN_API_KEY" \
-H "X-TALVIN-ORGANIZATION-ID: $TALVIN_ORG_ID" \
| jq '.[] | {email, candidate_status, interview: .interview.name}'
React instantly when a candidate is marked Selected (webhook):
POST /subscribewitheventType: "candidate_status_changed".- In your handler, check
payload.candidate_status === "SELECTED". - Advance the candidate in your ATS / notify the hiring manager.