> ## Documentation Index
> Fetch the complete documentation index at: https://docs.techeval.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Kovi Python SDK Code Examples and Integration Patterns

> Practical Python code examples for scheduling different interview types with Kovi—from initial screening to system design rounds with full error handling.

These ready-to-use Python examples cover the most common Kovi integration patterns. Copy, adapt, and run them directly in your hiring pipeline.

<Tip>
  Store all generated `interviewUrl` values in your database or ATS so you can track which candidates have completed their interviews.
</Tip>

***

## Basic Interview Scheduling

The example below schedules a **System Design** interview for a mid-to-senior AI Engineer. It is the fastest way to verify your integration end-to-end.

```python basic_schedule.py theme={null}
import os
from kovi import KoviClient

# Load your API key from the environment — never hardcode it
client = KoviClient(api_key=os.environ.get("KOVI_API_KEY"))

try:
    response = client.schedule_interview(
        # Job description ID from your Techeval dashboard
        job_id="JD-AI-7284",

        # Candidate details — Kovi emails the interview link automatically
        candidate_name="Alex Rivera",
        candidate_email="alex.rivera@example.com",
        mobile_number="+91-9999999999",

        # Interview configuration
        role="AI Engineer",
        interview_type="System Design",
        evaluation_level="Mid-Level to Senior",

        # Focus the interview on your production stack
        tech_stack=[
            "Python", "FastAPI", "GCP", "LangChain",
            "PostgreSQL", "Supabase", "Pinecone"
        ],

        # 45-minute session; link expires in 72 hours
        duration=45,
        pass_score=6.5,
        deadline_hours=72
    )

    print("✅ Interview scheduled successfully!")
    print(f"🔗 Send this link to the candidate: {response.get('interviewUrl')}")

except Exception as e:
    print(f"❌ Failed to schedule interview: {e}")
```

***

## Initial Screening for Multiple Candidates

Use a loop to schedule screening interviews for an entire candidate shortlist in one script run. Each iteration schedules independently so a single failure does not block the rest of the batch.

```python bulk_screening.py theme={null}
from kovi import KoviClient
import os

client = KoviClient(api_key=os.environ.get("KOVI_API_KEY"))

candidates = [
    {"name": "Alice Chen", "email": "alice@example.com", "mobile": "+91-9000000001"},
    {"name": "Bob Sharma", "email": "bob@example.com", "mobile": "+91-9000000002"},
]

for candidate in candidates:
    try:
        response = client.schedule_interview(
            job_id="JD-BACKEND-101",
            candidate_name=candidate["name"],
            candidate_email=candidate["email"],
            mobile_number=candidate["mobile"],
            role="Backend Engineer",
            interview_type="Initial Screening",
            evaluation_level="Mid-Level to Senior",
            tech_stack=["Python", "Django", "PostgreSQL"],
            duration=30,
            pass_score=6.5,
            deadline_hours=48
        )
        print(f"Scheduled for {candidate['name']}: {response.get('interviewUrl')}")
    except Exception as e:
        print(f"Failed for {candidate['name']}: {e}")
```

<Warning>
  Check your available credit balance before running a bulk loop. Scheduling 50 interviews requires 50 credits. Running out of credits mid-loop will cause the remaining candidates to fail. Top up at [techeval.ai](https://techeval.ai) before kicking off large batches.
</Warning>

***

## System Design Interview

Schedule a **Senior to Architect** level System Design round for a candidate being evaluated on distributed systems and cloud infrastructure.

```python system_design.py theme={null}
import os
from kovi import KoviClient

client = KoviClient(api_key=os.environ.get("KOVI_API_KEY"))

try:
    response = client.schedule_interview(
        # Reference the JD you created in the Techeval dashboard
        job_id="JD-DISTRIBUTED-SYS-42",

        # Senior-level candidate for a Staff Engineer role
        candidate_name="Priya Nair",
        candidate_email="priya.nair@example.com",
        mobile_number="+91-9888888888",

        role="Staff Engineer – Distributed Systems",
        interview_type="System Design",

        # Architect-level complexity: advanced follow-ups, trade-off analysis
        evaluation_level="Senior to Architect",

        # Scope the interview to your infrastructure stack
        tech_stack=[
            "Python", "Go", "Kafka", "Kubernetes",
            "AWS", "PostgreSQL", "Redis", "Terraform"
        ],

        # Full 60-minute session for deep architecture discussion
        duration=60,

        # Raise the bar for senior architect hires
        pass_score=7.0,

        # 48-hour window — creates urgency for active candidates
        deadline_hours=48
    )

    print("✅ System Design interview scheduled!")
    print(f"🔗 Candidate link: {response.get('interviewUrl')}")

except Exception as e:
    print(f"❌ Scheduling failed: {e}")
```

***

## Error Handling Best Practices

Robust error handling is essential when Kovi is embedded in an automated hiring pipeline. Follow these patterns to keep your integration reliable.

<CodeGroup>
  ```python robust_scheduling.py theme={null}
  import os
  import time
  import logging
  from kovi import KoviClient

  logging.basicConfig(level=logging.INFO)
  logger = logging.getLogger(__name__)

  client = KoviClient(api_key=os.environ.get("KOVI_API_KEY"))

  def schedule_with_logging(candidate: dict) -> str | None:
      """
      Schedule a single interview and return the URL on success,
      or None on failure. Always logs the outcome.
      """
      try:
          response = client.schedule_interview(
              job_id=candidate["job_id"],
              candidate_name=candidate["name"],
              candidate_email=candidate["email"],
              mobile_number=candidate["mobile"],
              role=candidate["role"],
              interview_type=candidate["interview_type"],
              evaluation_level=candidate["evaluation_level"],
              tech_stack=candidate["tech_stack"],
              duration=candidate["duration"],
              pass_score=candidate["pass_score"],
              deadline_hours=candidate["deadline_hours"]
          )
          url = response.get("interviewUrl")
          logger.info("Scheduled interview for %s → %s", candidate["name"], url)
          return url

      except Exception as e:
          # Log the full error — never swallow it silently
          logger.error(
              "Failed to schedule interview for %s: %s",
              candidate["name"], str(e)
          )
          return None


  def schedule_bulk(candidates: list[dict], delay_seconds: float = 0.5) -> dict:
      """
      Schedule interviews for a list of candidates.
      Introduces a small delay between requests to stay within rate limits.
      Returns a dict mapping candidate email → interviewUrl (or None on failure).
      """
      results = {}

      for i, candidate in enumerate(candidates):
          url = schedule_with_logging(candidate)
          results[candidate["email"]] = url

          # Avoid hammering the API — back off between requests
          if i < len(candidates) - 1:
              time.sleep(delay_seconds)

      return results
  ```

  ```python rate_limit_handling.py theme={null}
  import os
  import time
  import logging
  from kovi import KoviClient

  logger = logging.getLogger(__name__)
  client = KoviClient(api_key=os.environ.get("KOVI_API_KEY"))

  def schedule_with_retry(params: dict, max_retries: int = 3) -> str | None:
      """
      Schedule an interview with exponential back-off on rate-limit errors (429).
      Does NOT retry authentication errors (401) or credit failures (402).
      """
      for attempt in range(1, max_retries + 1):
          try:
              response = client.schedule_interview(**params)
              return response.get("interviewUrl")

          except Exception as e:
              error_message = str(e)

              # Do not retry auth or payment errors — they require human action
              if "401" in error_message:
                  logger.error("Invalid API key. Check your KOVI_API_KEY.")
                  return None
              if "402" in error_message:
                  logger.error("Insufficient credits. Top up at techeval.ai.")
                  return None

              # On rate limit (429), wait before retrying
              if "429" in error_message:
                  wait = 2 ** attempt  # 2s, 4s, 8s
                  logger.warning(
                      "Rate limited (attempt %d/%d). Retrying in %ds…",
                      attempt, max_retries, wait
                  )
                  time.sleep(wait)
                  continue

              # For any other error, log and bail out
              logger.error("Unexpected error on attempt %d: %s", attempt, error_message)
              return None

      logger.error("All %d retry attempts exhausted.", max_retries)
      return None
  ```
</CodeGroup>

### Key error handling rules

* **Always log the full exception message.** Silent failures in bulk pipelines are nearly impossible to debug retroactively.
* **Do not retry immediately on `429` (rate limit).** Use exponential back-off (2 s, 4 s, 8 s…) between retry attempts.
* **Check your credit balance before bulk scheduling.** Running out of credits mid-batch leaves some candidates without an interview link and no automatic notification to you.
* **Do not retry `401` or `402` errors.** These require manual intervention — a bad API key won't fix itself on retry, and credits won't appear automatically.
* **Persist every `interviewUrl` you receive.** If your process crashes after scheduling, you will need the stored URLs to avoid double-scheduling candidates.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.