Skip to main content

zoplio (Python SDK)

Official Zoplio Python SDK for the Zoplio API v1. MIT licensed.

Zoplio schedules meetings for you: you say who to invite and roughly when, Zoplio negotiates with every invitee over WhatsApp/email and confirms a slot.

Requires Python >= 3.10. Depends on httpx.

Install

pip install zoplio

Roles

The account that owns the API key is the organizer. participants are the people Zoplio invites. To arrange a meeting for someone else, set organizer.

  • You are a Zoplio user like anyone on WhatsApp: a meeting you create runs on your calendar, in your timezone and working hours, Zoplio tells you when the invitees answer, and it counts against your plan.
  • Every participant (1-8) is invited; one is enough. Your own number or e-mail as a participant is rejected with validation_failed.
  • organizer is the optional on-behalf mode (an agency booking for a client): that person is then the organizer, every participant is still invited, and the meeting still bills to your account.

Usage

from zoplio import ZoplioClient, ZoplioError

zoplio = ZoplioClient(api_key="zpl_...")  # base_url defaults to https://api.zoplio.com

# Create a meeting. You are the organizer; Zoplio invites Jana.
created = zoplio.schedule_meeting(
    participants=[{"phone": "+420777123456", "name": "Jana"}],
    title="Intro call",
    duration_minutes=30,
    preferred_date="2026-09-16",
    preferred_time="14:00",
    timezone="Europe/Prague",  # always send it with preferred_time
    idempotency_key="order-42-intro-call",  # optional, safe retries
)
print(created["meetingId"], created["status"], created["proposedSlots"])

# Poll status (or use webhooks instead). Each entry of meeting["participants"]
# carries status, attending and role ("organizer" | "participant").
meeting = zoplio.get_meeting(created["meetingId"])

# List / reschedule / cancel.
zoplio.list_meetings(status="confirmed", limit=10)
zoplio.reschedule_meeting(
    created["meetingId"],
    preferred_date="2026-09-17",
    preferred_time="10:00",
    timezone="Europe/Prague",
    idempotency_key="order-42-intro-call-move-1",  # a retry replays instead of opening another round
)
zoplio.cancel_meeting(created["meetingId"])

On behalf of someone else: set organizer. Petr is then the organizer (his calendar and timezone; Zoplio tells him the meeting is being arranged and again once it confirms) and Jana is invited:

zoplio.schedule_meeting(
    organizer={"email": "petr@example.com", "name": "Petr"},
    participants=[{"phone": "+420777123456", "name": "Jana"}],
    title="Intro call",
    preferred_date="2026-09-16",
    preferred_time="14:00",
    timezone="Europe/Prague",
)

Scheduling mode is picked by the date fields you send: exact (preferred_date + preferred_time + timezone), day (preferred_date only), range (earliest_date + latest_date) or open ask (open_ask=True plus the window). With no date fields Zoplio proposes one slot per working day over the next seven days at the start of the organizer's working hours (09:00 by default), rendered in each invitee's own timezone.

Errors

Every non-2xx response raises ZoplioError with the contract envelope:

try:
    zoplio.get_meeting("nope")
except ZoplioError as err:
    err.status_code  # 404
    err.code         # 'not_found' | 'unauthorized' | 'rate_limited' | 'validation_failed' | 'conflict' | 'quota_exceeded' | 'upstream_error'
    str(err)         # human-readable message
    err.details      # [{"field", "message"}] on validation_failed

quota_exceeded (HTTP 402) means your account's free-plan limit was reached this calendar month, the same limits as for any Zoplio user: 3 confirmed meetings and 15 meeting requests per calendar month (UTC). Meetings still being arranged count against the 3 until they confirm or fall through; str(err) says which limit it was.

Webhooks

# Subscribe. The secret is returned exactly once.
hook = zoplio.create_webhook(
    url="https://example.com/zoplio-hook",
    events=["meeting.confirmed", "meeting.rescheduled", "meeting.cancelled"],
    # omit `events` for all five: meeting.created, meeting.confirmed,
    # meeting.cancelled, meeting.rescheduled, negotiation.failed
)
save_secret(hook["secret"])  # whsec_...

zoplio.list_webhooks()
zoplio.delete_webhook(hook["id"])

meeting.rescheduled fires the moment a confirmed meeting re-opens to move; its payload carries previousSlot, and a fresh meeting.confirmed (or a cancellation) follows when the renegotiation resolves.

Verify deliveries with the static helper. Pass the RAW request body:

# e.g. Flask
@app.post("/zoplio-hook")
def zoplio_hook():
    ok = ZoplioClient.verify_webhook_signature(
        request.get_data(),                          # raw bytes
        request.headers.get("X-Zoplio-Signature", ""),
        os.environ["ZOPLIO_WEBHOOK_SECRET"],         # whsec_...
    )
    if not ok:
        return "", 401
    delivery = request.get_json()  # {"event", "payload", "timestamp"}
    return "", 200

Development

pip install -e ".[dev]"
python -m pytest
python -m mypy src/zoplio

Metadata

Release files for zoplio 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for zoplio 0.3.0
File Size Uploaded
zoplio-0.3.0.tar.gz 12.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for zoplio 0.3.0
File Interpreter ABI Platform
zoplio-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 23.0 kB

Release files / zoplio-0.3.0.tar.gz

Download URL zoplio-0.3.0.tar.gz
Size 12.7 kB
Tags Source
SHA-256 checksum
How to use checksums
e70df66c154e8d365d41b66381f676e7c2511c87d077de796f5fde0b05d927fe
BLAKE2b-256 checksum
How to use checksums
3f3eadd172efbac6a55a9ba1218fbec0a061a5db44fb6c7231ea47b7d923bec3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.6

Release files / zoplio-0.3.0-py3-none-any.whl

Download URL zoplio-0.3.0-py3-none-any.whl
Size 10.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
67006ccdb8a264e66ff19ceb3693078d9d6d7067543e6fdac43943fbbc819865
BLAKE2b-256 checksum
How to use checksums
5aeea4bb3d7ed591e210c6cc9cdc64fe6a47923d4a9281fccd8f96836520ca19
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.6

Release history Release notifications | RSS feed

0.3.1

2 release files

This release

0.3.0 This release

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page