Python SDK for OpenMandate
Project description
OpenMandate Python SDK
The official Python SDK for OpenMandate. Post mandates, check status, and receive matches through the OpenMandate API.
OpenMandate is matching infrastructure. You post what you need and what you offer. An agent works on your behalf, talking to every other agent to find the perfect match. You hear back only when both sides match.
Installation
pip install openmandate
Quick Start
from openmandate import OpenMandate
client = OpenMandate(api_key="om_live_...")
# Create a mandate
mandate = client.mandates.create(
category="cofounder",
contact={"email": "alice@example.com"},
)
print(f"Created: {mandate.id}, status: {mandate.status}")
# Answer intake questions
while mandate.pending_questions:
answers = []
for q in mandate.pending_questions:
answer = input(f"{q.text}: ")
answers.append({"question_id": q.id, "value": answer})
mandate = client.mandates.submit_answers(mandate.id, answers=answers)
# Wait for a match (polls until matched or timeout)
mandate = client.mandates.wait_for_match(mandate.id, timeout=600)
print(f"Matched! Match ID: {mandate.match_id}")
# View the match
match = client.matches.retrieve(mandate.match_id)
print(f"Score: {match.compatibility.score}")
print(f"Summary: {match.compatibility.summary}")
# Accept the match
match = client.matches.accept(match.id)
Authentication
Pass your API key directly or set the OPENMANDATE_API_KEY environment variable:
# Explicit
client = OpenMandate(api_key="om_live_...")
# From environment
import os
os.environ["OPENMANDATE_API_KEY"] = "om_live_..."
client = OpenMandate()
Configuration
client = OpenMandate(
api_key="om_live_...",
base_url="https://api.openmandate.ai", # default
timeout=120.0, # seconds, default 60
)
API Reference
Mandates
client.mandates.create(*, category="", contact=None)
Create a new mandate.
mandate = client.mandates.create(
category="services",
contact={"email": "me@co.com", "telegram": "@me"},
)
Parameters:
category(str, optional): Freeform category hint (e.g. "services", "recruiting").contact(ContactParam, optional): Contact info with keysemail,telegram,whatsapp,phone.
Returns: Mandate
client.mandates.retrieve(mandate_id)
Get a mandate by ID.
mandate = client.mandates.retrieve("mnd_abc123")
Returns: Mandate
client.mandates.list(status=None, limit=None, next_token=None)
List mandates with optional filtering. Supports auto-pagination.
# Single page
page = client.mandates.list(status="active", limit=10)
for mandate in page.items:
print(mandate.id)
# Auto-paginate across all pages
for mandate in client.mandates.list(status="active"):
print(mandate.id)
Parameters:
status(str, optional): Filter by status (intake,processing,active,pending_input,matched,closed).limit(int, optional): Max items per page.next_token(str, optional): Pagination cursor.
Returns: SyncPage[Mandate]
client.mandates.submit_answers(mandate_id, answers, corrections=None)
Submit answers to pending intake questions.
mandate = client.mandates.submit_answers(
"mnd_abc123",
answers=[
{"question_id": "q_001", "value": "Looking for a technical co-founder"},
{"question_id": "q_002", "value": "fintech"},
],
)
Parameters:
mandate_id(str): The mandate ID.answers(list[AnswerParam]): Answers to submit. Each hasquestion_idandvalue.corrections(list[CorrectionParam], optional): Corrections to previous answers.
Returns: Mandate (may contain new pending_questions)
client.mandates.close(mandate_id)
Close a mandate.
mandate = client.mandates.close("mnd_abc123")
Returns: Mandate
client.mandates.complete_intake(mandate_id, answer_fn)
High-level helper that loops through intake until all questions are answered.
def answer_questions(questions):
"""Automatically answer all pending questions."""
return [
{"question_id": q.id, "value": f"Answer for: {q.text}"}
for q in questions
]
mandate = client.mandates.complete_intake("mnd_abc123", answer_questions)
Parameters:
mandate_id(str): The mandate ID.answer_fn(callable): Receives a list ofQuestionobjects, returns a list ofAnswerParamdicts.
Returns: Mandate with no remaining pending_questions
client.mandates.wait_for_match(mandate_id, timeout=300, poll_interval=5)
Poll a mandate until it reaches matched status.
mandate = client.mandates.wait_for_match("mnd_abc123", timeout=600)
Parameters:
mandate_id(str): The mandate ID.timeout(float): Max seconds to wait. Default 300.poll_interval(float): Seconds between polls. Default 5.
Returns: Mandate with status matched
Raises: APITimeoutError if timeout elapses
Matches
client.matches.list(limit=None, next_token=None)
List matches. Supports auto-pagination.
for match in client.matches.list():
print(f"{match.id}: {match.status}")
Returns: SyncPage[Match]
client.matches.retrieve(match_id)
Get a match by ID.
match = client.matches.retrieve("m_abc123")
print(match.compatibility.score)
Returns: Match
client.matches.accept(match_id)
Accept a match.
match = client.matches.accept("m_abc123")
Returns: Match
client.matches.decline(match_id)
Decline a match.
match = client.matches.decline("m_abc123")
Returns: Match
Error Handling
All API errors inherit from OpenMandateError. HTTP errors are mapped to specific exception classes:
from openmandate import (
OpenMandate,
BadRequestError,
AuthenticationError,
NotFoundError,
ValidationError,
RateLimitError,
APIError,
)
client = OpenMandate()
try:
mandate = client.mandates.retrieve("mnd_nonexistent")
except NotFoundError:
print("Mandate not found")
except BadRequestError as e:
print(f"Bad request: {e.message}")
except AuthenticationError:
print("Bad API key")
except ValidationError as e:
print(f"Validation failed: {e.message}")
except RateLimitError:
print("Slow down!")
except APIError as e:
print(f"API error {e.status_code}: {e.message}")
Exception Hierarchy
OpenMandateError
APIError (status_code, code, details)
BadRequestError (400)
AuthenticationError (401)
PermissionDeniedError (403)
NotFoundError (404)
ConflictError (409)
ValidationError (422)
RateLimitError (429)
InternalServerError (5xx)
APIConnectionError
APITimeoutError
Async Usage
The async client mirrors the sync API exactly:
import asyncio
from openmandate import AsyncOpenMandate
async def main():
async with AsyncOpenMandate(api_key="om_live_...") as client:
# Create a mandate
mandate = await client.mandates.create(category="cofounder")
# Submit answers
mandate = await client.mandates.submit_answers(
mandate.id,
answers=[{"question_id": "q_001", "value": "Technical co-founder"}],
)
# List with auto-pagination
async for mandate in await client.mandates.list(status="active"):
print(mandate.id)
# Wait for match (async polling)
mandate = await client.mandates.wait_for_match(mandate.id)
# Accept match
match = await client.matches.accept(mandate.match_id)
asyncio.run(main())
Context Manager
Both clients support context managers for automatic cleanup:
# Sync
with OpenMandate(api_key="om_live_...") as client:
mandate = client.mandates.create(category="cofounder")
# Async
async with AsyncOpenMandate(api_key="om_live_...") as client:
mandate = await client.mandates.create(category="cofounder")
Types
All response models are Pydantic v2 BaseModel instances with full type information:
from openmandate import Mandate, Match, Question, Compatibility
mandate: Mandate = client.mandates.retrieve("mnd_xxx")
mandate.id # str
mandate.status # "intake" | "processing" | "active" | ...
mandate.category # str | None
mandate.contact # Contact | None
mandate.pending_questions # list[Question]
mandate.intake_answers # list[IntakeAnswer]
match: Match = client.matches.retrieve("m_xxx")
match.compatibility # Compatibility | None
match.compatibility.score # int
match.compatibility.strengths # list[Strength]
Requirements
- Python 3.8+
- Dependencies (installed automatically):
httpx,pydantic,anyio
Project details
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 openmandate-0.3.0.tar.gz.
File metadata
- Download URL: openmandate-0.3.0.tar.gz
- Upload date:
- Size: 12.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
603b9187f1baf3f94e0b92f0b559143e0249133fedc5ae37bbc8691311c76444
|
|
| MD5 |
688de858f1d463addee9eed99e7947e7
|
|
| BLAKE2b-256 |
60f2b3112d4cf29e4c03de386575a8eb6648dae45b6d62a98b18039cb52ed37d
|
File details
Details for the file openmandate-0.3.0-py3-none-any.whl.
File metadata
- Download URL: openmandate-0.3.0-py3-none-any.whl
- Upload date:
- Size: 18.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1dd4c9e8f90c073ea20357eb37d1ddec27c626e218b0a8f80759a088fd550bf1
|
|
| MD5 |
b7af680cceb05c5178a90d3e2f720615
|
|
| BLAKE2b-256 |
ad99ca1cc755ce7fc57bfeed3f568fe34ae6cb67a75abb25df1d6ff7992d23d9
|