Calendar Scheduling Agent

Your LLM can extract next Tuesday at 2pm from a message. But turning that into 2026-04-07T14:00:00-04:00 requires date math, timezone handling, and ambiguity detection. That's what Biruni does.

User says
Book a team sync next Tuesday at 2pm Eastern.
extract()Your LLM resolve_time()Biruni API create_event()Your calendar reply()Your chat

The agent

calendar_agent.py
import httpx, os
from datetime import date

BIRUNI_URL = "https://biruni.dev/v1/resolve"


# ── Stubs: replace with your integrations ────────────────────

async def create_event(title: str, start: str, tz: str):
    """Create a calendar event. (Google Calendar, Outlook, CalDAV ...)"""
    ...

async def reply(text: str):
    """Send a response back to the user. (Slack, chat UI, email ...)"""
    ...

def extract(message: str) -> dict:
    """
    Use your LLM to pull structured fields from the raw message.
    Returns: { "title": str, "phrase": str, "tz": str }
    """
    ...


# ── Biruni integration ──────────────────────────────────────

async def resolve_time(phrase: str, tz: str) -> dict:
    async with httpx.AsyncClient() as client:
        r = await client.post(
            BIRUNI_URL,
            headers={"Authorization": f"Bearer {os.environ['BIRUNI_API_KEY']}"},
            json={
                "expression": phrase,
                "reference_date": date.today().isoformat(),
                "timezone": tz,
            },
        )
        r.raise_for_status()
        return r.json()


# ── Agent entry point ────────────────────────────────────────

async def handle(message: str):
    fields = extract(message)

    result = await resolve_time(fields["phrase"], fields["tz"])

    match result["status"]:
        case "resolved":
            await create_event(fields["title"], result["resolved"], fields["tz"])
            await reply(f"Done — {fields['title']} at {result['resolved']}.")

        case "ambiguous":
            opts = "\n".join(
                f"  {i+1}. {a['interpretation']}"
                for i, a in enumerate(result["alternatives"])
            )
            await reply(f"That time is ambiguous:\n{opts}")

        case _:
            await reply("Couldn't parse that time — could you rephrase?")