Talvin AI · Developer Reference · v1

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.

https://app.talvin.ai/api/v1

1 · Getting your credentials

You need two values, both in the Talvin dashboard under Settings → Integrations:

CredentialWhere to find itExample
Organization IDThe “Organization ID” card at the top of the Integrations pageorg_2abc…
API KeyAPI Keys tab → Create API Key → choose Other (or a platform)talvin_a1b2c3…
⚠️ Your API key is shown only once, at creation time. Store it in a secrets manager or environment variable. If you lose it, revoke the key and create a new one. The key grants read access to your organization's interview and candidate data — never commit it to source control or ship it in client-side code.

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:

HeaderMeaning
X-RateLimit-LimitRequests allowed in the current window
X-RateLimit-RemainingRequests remaining in the current window

When exceeded, the API returns 429 with { "error": "Rate limit exceeded" } — back off briefly and retry.

4 · Endpoints

GET/whoami

Validates your credentials. Returns the API key's id and name.

{ "success": true, "data": { "id": "0f4c1…", "name": "Other" } }
GET/interviews

Lists all interviews in your organization, newest first.

FieldTypeDescription
idstringInterview ID
namestringInterview / role name
descriptionstringInterview description
question_countnumberNumber of main questions
time_durationstringPlanned duration in minutes
questionsarray[{ id, question, follow_up_count }]
is_activebooleanWhether 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
  }
]
GET/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.

Note: for compatibility reasons this endpoint returns an array containing one interview, not a bare object.
GET/responses

Lists the 100 most recent candidate responses across all your interviews, newest first.

FieldTypeDescription
idnumberResponse ID
created_atstringISO 8601 — when the candidate started
namestringCandidate name
emailstringCandidate email
call_idstringVoice-call ID
durationnumberCall duration in seconds
candidate_statusstringNO_STATUS · POTENTIAL · SELECTED · NOT_SELECTED
interviewobject{ 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

eventTypeFired whenPayload
new_interviewA new interview is created in your organizationThe interview object (id, name, question_count, time_duration, questions, description)
candidate_status_changedA candidate's status is updated in the dashboard (e.g. marked Selected)The full response object (same shape as GET /responses items)
new_responseReserved for future use; not currently emitted
POST/subscribe
curl -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.

DELETE/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

6 · Errors

Errors return a JSON body { "error": "message" } with standard status codes:

StatusMeaning
400Malformed request (e.g. missing hookUrl)
401Missing or invalid API key / organization ID
404Resource not found (or belongs to another organization)
429Rate limit exceeded — retry after a short delay
500Something 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):

  1. POST /subscribe with eventType: "candidate_status_changed".
  2. In your handler, check payload.candidate_status === "SELECTED".
  3. Advance the candidate in your ATS / notify the hiring manager.
No-code option: if you'd rather not write any code, use the Zapier integration (Settings → Integrations → Platforms → Zapier) — it uses this same API under the hood and connects Talvin to 6,000+ apps.