Typed Python SDK for the Mukhtabir API
Project description
Mukhtabir Python SDK
Typed sync and async clients for the Mukhtabir API.
- Python
3.10+ - Default base URL:
https://mukhtabir.hbku.edu.qa/api/v1 - Import clients from
mukhtabir, models frommukhtabir.models, errors frommukhtabir.errors, and webhook helpers frommukhtabir.webhooks
Install
Install a published release from PyPI:
pip install mukhtabir
Install from a local checkout of this repository:
pip install ./python
For local SDK development:
cd python
pip install -e ".[dev]"
Quick Start
import logging
from mukhtabir import MukhtabirClient
from mukhtabir.models import CreateInterviewRequest
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
with MukhtabirClient(api_key="mk_live_your_key") as client:
created = client.interviews.create(
CreateInterviewRequest(
role="Senior Software Engineer",
type="technical",
level="senior",
duration=30,
techstack=["Python", "PostgreSQL"],
visibility="restricted",
)
)
interview = client.interviews.get(created.data.interview_id)
logger.info("Interview %s", interview.data.id)
logger.info("Role %s", interview.data.role)
All resource methods return typed envelope objects:
ApiResponse[T]withsuccess,data, andmetaPaginatedResponse[T]withsuccess,data,pagination, andmeta
Async Client
The async client mirrors the sync API.
import logging
from mukhtabir import AsyncMukhtabirClient
from mukhtabir.models import CreateCandidateRequest
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
async with AsyncMukhtabirClient(api_key="mk_live_your_key") as client:
candidate = await client.candidates.create(
CreateCandidateRequest(
email="candidate@example.com",
name="Sarah Al-Rashid",
)
)
logger.info("Interview URL %s", candidate.data.interview_url)
Nested Interview APIs
The interviews resource also exposes nested mutation APIs for interview questions, subquestions, and evaluation criteria.
from mukhtabir import MukhtabirClient
from mukhtabir.models import (
AddCriteriaRequest,
AddQuestionRequest,
AddSubquestionRequest,
UpdateCriteriaRequest,
UpdateQuestionRequest,
UpdateSubquestionRequest,
)
with MukhtabirClient(api_key="mk_live_your_key") as client:
question = client.interviews.add_question(
"interview-id",
AddQuestionRequest(
question="How would you design a rate limiter?",
subquestions=["What would you store in Redis?"],
),
)
client.interviews.update_question(
"interview-id",
question.data.question_id,
UpdateQuestionRequest(order_index=1),
)
subquestion = client.interviews.add_subquestion(
"interview-id",
question.data.question_id,
AddSubquestionRequest(subquestion="How would you handle burst traffic?"),
)
client.interviews.update_subquestion(
"interview-id",
question.data.question_id,
subquestion.data.subquestion_id,
UpdateSubquestionRequest(disabled=False),
)
criteria = client.interviews.add_criteria(
"interview-id",
AddCriteriaRequest(
criteria_title="System design",
description="Tradeoffs, scaling, and operational clarity",
),
)
client.interviews.update_criteria(
"interview-id",
criteria.data.criteria_id,
UpdateCriteriaRequest(order_index=0),
)
client.interviews.delete_subquestion(
"interview-id",
question.data.question_id,
subquestion.data.subquestion_id,
)
client.interviews.delete_criteria(
"interview-id",
criteria.data.criteria_id,
)
client.interviews.delete_question(
"interview-id",
question.data.question_id,
)
Persist question_id, subquestion_id, and criteria_id returned by create calls. GET /interviews/:id now also exposes stable nested IDs and ordering metadata on the interview detail payload, so existing nested items can be discovered from the read surface as well. Read payloads use camelCase orderIndex, while mutation inputs still use snake_case order_index.
Sync/Async Maintenance
Sync and async endpoint definitions are maintained from a shared source of truth in mukhtabir/resources/_request_specs.py.
When adding or changing an endpoint:
- define the request path, parser, and payload shape in
_request_specs.py - add matching sync and async resource wrappers with the same public method name
- add paired sync/async tests for the mirrored surface so behavior does not drift
Live Integration
Run the live API suite from the package root:
./scripts/run-live-integration.sh -q
The script requires MUKHTABIR_API_KEY, uses MUKHTABIR_BASE_URL when provided, and sets
MUKHTABIR_INTEGRATION=1 automatically. To keep ./scripts/run-live-integration.sh as the
full-coverage entrypoint instead of a partial subset, it also requires these environment
variables:
MUKHTABIR_INTEGRATION_INTERVIEW_IDMUKHTABIR_INTEGRATION_FEEDBACK_IDMUKHTABIR_INTEGRATION_EXTERNAL_FEEDBACK_IDMUKHTABIR_INTEGRATION_EXTERNAL_RECORDING_URLMUKHTABIR_INTEGRATION_CANDIDATE_EMAILMUKHTABIR_INTEGRATION_WEBHOOK_IDMUKHTABIR_INTEGRATION_LIMITED_API_KEY
The fixture IDs and URLs are not secrets. Pass them explicitly in CI or on the command line rather
than storing them in .env. MUKHTABIR_INTEGRATION_LIMITED_API_KEY is a secret and should be
handled like any other API key.
Client Configuration
MukhtabirClient and AsyncMukhtabirClient accept:
api_key: required bearer tokenbase_url: override the default API base URLtimeout:float,httpx.Timeout, orNone(default10.0)max_retries: retry count for idempotent requests only (default2)http_client: pass your ownhttpx.Clientorhttpx.AsyncClient
Retries apply to idempotent methods (GET, DELETE, HEAD, OPTIONS) for transport failures and retryable responses such as 429 and 5xx.
Pagination
List endpoints return PaginatedResponse[...]. For auto-paging, use the iterator helpers:
import logging
from mukhtabir import MukhtabirClient
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
with MukhtabirClient(api_key="mk_live_your_key") as client:
for interview in client.interviews.iter_all(page_size=50):
logger.info("Interview %s (%s)", interview.id, interview.role)
Available auto-paging helpers:
client.interviews.iter_all()client.interviews.iter_all_results(interview_id)client.candidates.iter_all()client.webhooks.iter_all()client.webhooks.iter_all_deliveries(webhook_id)
Resources
| Resource | Methods |
|---|---|
client.interviews |
create, list, iter_all, get, update, delete, add_question, update_question, delete_question, add_subquestion, update_subquestion, delete_subquestion, add_criteria, update_criteria, delete_criteria, publish, invite, list_results, iter_all_results, get_analytics |
client.candidates |
create, list, iter_all, get |
client.feedback |
get, get_transcript, get_recording_url |
client.webhooks |
create, list, iter_all, get, update, delete, test, list_deliveries, iter_all_deliveries |
Request and response bodies are exposed as typed dataclasses.
CreateInterviewRequest,UpdateInterviewRequestAddQuestionRequest,UpdateQuestionRequest,QuestionCreateResultAddSubquestionRequest,UpdateSubquestionRequest,SubquestionCreateResultAddCriteriaRequest,UpdateCriteriaRequest,CriteriaCreateResultCreateCandidateRequest,InviteCandidateRequestInterviewDetails,CandidateDetails,FeedbackDetails
Import interview, candidate, feedback, and shared envelope models from mukhtabir.models.
Import webhook request/response models and payload helpers from mukhtabir.webhooks.
CreateWebhookRequest,UpdateWebhookRequestWebhookDetails,WebhookCreateResult,WebhookPayload
Publishing
GitHub Actions publishes tags named python-sdk-vX.Y.Z to PyPI through
.github/workflows/python-sdk-release.yml.
- Update
src/mukhtabir/_version.py. - Merge the release commit to
main. - Ensure the PyPI project
mukhtabirtrusts this repository'spypi-releaseGitHub Actions environment before the first release. - Push a tag named
python-sdk-vX.Y.Z. - If the Python release repository secrets and vars are configured, the workflow also runs the live integration gate before publishing; otherwise it skips that step.
Manual workflow runs build and validate the distribution artifacts without publishing them.
Errors
All SDK exceptions inherit from MukhtabirError.
- API errors:
AuthenticationError,PermissionError,ValidationError,NotFoundError,ConflictError,RateLimitError,ServerError,APIError - Client/runtime errors:
TransportError,UnexpectedResponseError
Exceptions may include status_code, code, details, request_id, and retry_after.
import logging
from mukhtabir import MukhtabirClient
from mukhtabir.errors import MukhtabirError
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
try:
with MukhtabirClient(api_key="mk_live_your_key") as client:
client.feedback.get("feedback-id")
except MukhtabirError as exc:
logger.exception("Feedback request failed: %s", exc)
logger.info("Request ID %s", exc.request_id)
Webhook Verification
The SDK includes helpers for signature verification and payload parsing:
import logging
from mukhtabir.webhooks import (
parse_webhook_headers,
parse_webhook_payload,
verify_webhook_signature,
)
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def handle_webhook(headers: dict[str, str], body: bytes, secret: str) -> None:
webhook_headers = parse_webhook_headers(headers)
if not verify_webhook_signature(
body=body,
signature=webhook_headers.signature,
timestamp=webhook_headers.timestamp,
secret=secret,
tolerance_seconds=300,
):
raise ValueError("Invalid webhook signature")
payload = parse_webhook_payload(body)
logger.info("Webhook event %s", payload.event)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mukhtabir-0.1.1.tar.gz.
File metadata
- Download URL: mukhtabir-0.1.1.tar.gz
- Upload date:
- Size: 41.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
201c845ea5149db8319185c76f06caae576c343d4bd0f41e8287c878fffd373e
|
|
| MD5 |
69b534615410b0e587e6c6274afc2df6
|
|
| BLAKE2b-256 |
d33882d3f067847a4653e0b9251be78000976db5930578818fec4411106183b0
|
Provenance
The following attestation bundles were made for mukhtabir-0.1.1.tar.gz:
Publisher:
python-sdk-release.yml on voramind/mukhtabir-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mukhtabir-0.1.1.tar.gz -
Subject digest:
201c845ea5149db8319185c76f06caae576c343d4bd0f41e8287c878fffd373e - Sigstore transparency entry: 1258407860
- Sigstore integration time:
-
Permalink:
voramind/mukhtabir-sdk@e842411f31f920ee4a9e51effa9b7977a12c89c3 -
Branch / Tag:
refs/tags/python-sdk-v0.1.1 - Owner: https://github.com/voramind
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-sdk-release.yml@e842411f31f920ee4a9e51effa9b7977a12c89c3 -
Trigger Event:
push
-
Statement type:
File details
Details for the file mukhtabir-0.1.1-py3-none-any.whl.
File metadata
- Download URL: mukhtabir-0.1.1-py3-none-any.whl
- Upload date:
- Size: 38.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f723644ada77c7b2ec2d4050c2bedeb7e8b0a362486025fc1c50306d0717c5be
|
|
| MD5 |
2f01c482dbcd7de4f4a11353ae840787
|
|
| BLAKE2b-256 |
09b84ab292b4a4a394644abba132abc39bbb72939cf9c9fbda9f070f584dd9a7
|
Provenance
The following attestation bundles were made for mukhtabir-0.1.1-py3-none-any.whl:
Publisher:
python-sdk-release.yml on voramind/mukhtabir-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mukhtabir-0.1.1-py3-none-any.whl -
Subject digest:
f723644ada77c7b2ec2d4050c2bedeb7e8b0a362486025fc1c50306d0717c5be - Sigstore transparency entry: 1258407932
- Sigstore integration time:
-
Permalink:
voramind/mukhtabir-sdk@e842411f31f920ee4a9e51effa9b7977a12c89c3 -
Branch / Tag:
refs/tags/python-sdk-v0.1.1 - Owner: https://github.com/voramind
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-sdk-release.yml@e842411f31f920ee4a9e51effa9b7977a12c89c3 -
Trigger Event:
push
-
Statement type: