Python SDK

vorlek is the Python 3.10+ client for the Vorlek API. It wraps the six tool endpoints with sync and async clients, generated OpenAPI result aliases, automatic ULID idempotency keys, and typed exceptions.

pip install vorlek v1.2.1 · sync + async · typed errors PyPI

Quickstart

import os

from vorlek import VorlekClient

with VorlekClient(api_key=os.environ["VORLEK_API_KEY"]) as client:
    result = client.contact.upsert(
        provider="sendgrid",
        email="jamie@example.com",
        first_name="Jamie",
        properties={"plan": "free"},
    )

print(result.data["contact_id"])
print(result.meta.request_id)

Method reference

Argument shapes are generated from the public OpenAPI schema at vorlek.dev/openapi.json. Providers are sendgrid, mailchimp, and klaviyo.

NamespaceMethodRequired argumentsReturns
contactupsert(...)provider, emailUpsertContactResult
connectionstatus(...)providerGetConnectionStatusResult
sendtransactional(...)provider, to, subjectSendTransactionalResult
campaignstats(...)provider, campaign_idGetCampaignStatsResult
campaignlist(...)providerListCampaignsResult
templatelist(...)providerListTemplatesResult
catalogget()noneCatalogResult
operationsget(request_id)request_idOperationLookupResult

Catalog discovery

Call client.catalog.get() before planning a multi-provider workflow. The response tells agents which provider/tool cells are supported, which connection config is required, whether the current account has each provider connected, and that the current receipt lookup identifier is meta.request_id.

catalog = client.catalog.get()
for tool in catalog.data["tools"]:
    print(tool["name"], tool["providers"])

Operation lookup

Every successful write returns meta.request_id. Treat that value as the operation receipt id and pass it to client.operations.get(request_id) when an agent needs same-surface observability without raw REST.

result = client.contact.upsert(provider="sendgrid", email="test@example.com")
operation = client.operations.get(result.meta.request_id)
print(operation.data["status"], operation.data["duration_ms"])

Contact readback

client.contact.get reads one contact by email so agents can verify an upsert_contact result without leaving the SDK surface.

readback = client.contact.get(provider="sendgrid", email="test@example.com")
print(readback.data["found"], readback.data["contact"])

Response shape

Successful calls return a frozen VorlekResult. data is the normalized tool payload. meta includes request_id, quota state, rate-limit state merged from X-RateLimit-* headers, and idempotency replay state when present.

result.data is parsed JSON: at runtime it is dict[str, object], even though the per-tool aliases (UpsertContactResult, SendTransactionalResult, and the rest) point at the generated dataclasses for type-checker convenience. Use subscript syntax — result.data["contact_id"], not result.data.contact_id.

Response detail

Tool methods accept detail="minimal", detail="standard", or detail="full". minimal returns only essential metadata, standard is the default, and full includes operation lookup metadata.

result = client.contact.upsert(
    provider="sendgrid",
    email="test@example.com",
    detail="minimal",
)
result = client.template.list(provider="mailchimp", limit=10)

print(result.data["templates"])
print(result.meta.request_id)
print(result.meta.ratelimit.remaining if result.meta.ratelimit else None)

Template search

client.template.list accepts query for page-scoped template search across id, name, and subject. When the returned page has no match, inspect data["search"]["no_match"] for the next action.

result = client.template.list(provider="sendgrid", query="welcome")
print(result.data["templates"], result.data.get("search"))

Idempotency

Every tool call gets a fresh ULID idempotency key by default. Pass idempotency_key when retrying the same logical operation; the same key with the same request body is safe to call twice.

result = client.contact.upsert(
    provider="mailchimp",
    email="jamie@example.com",
    idempotency_key="01HV0011V0110011V011001100",
)
print(result.meta.idempotency.replay if result.meta.idempotency else False)

from_ parameter

Python reserves from, so transactional sends use from_. The SDK serializes it as from before sending the request.

client.send.transactional(
    provider="sendgrid",
    to="jamie@example.com",
    from_="updates@example.com",
    subject="Welcome",
    text="Thanks for trying Vorlek.",
)

Error classes

All API failures raise VorlekError subclasses. Check err.code, err.retry_safe, err.http_status, err.request_id, and err.provider. Network failures are SDK-synthesized and have http_status == 0.

ClassCoderetry_safeUse case
VorlekAuthErrorAUTH_MISSING, AUTH_INVALID, AUTH_FORBIDDENfalseMissing, invalid, or under-scoped Vorlek API key.
VorlekConnectionErrorCONNECTION_NOT_FOUND, CONNECTION_INVALIDfalseProvider is not connected or needs credential rotation.
VorlekValidationErrorINVALID_PARAMS, FIELD_TYPE_MISMATCH, PAYLOAD_TOO_LARGEfalseRequest body or provider field type needs correction.
VorlekToolNotSupportedErrorTOOL_NOT_SUPPORTEDfalseThe selected provider/tool pair is intentionally unavailable.
VorlekQuotaExceededErrorQUOTA_EXCEEDEDtruePlan-period quota is exhausted; wait for reset or upgrade.
VorlekRateLimitedErrorRATE_LIMITEDtrueShort-window throttling; honor Retry-After.
VorlekIdempotencyConflictErrorIDEMPOTENCY_CONFLICTfalseSame idempotency key was reused with a different request body.
VorlekProviderAuthInvalidErrorPROVIDER_AUTH_INVALIDfalseProvider key is invalid, expired, or missing required scopes.
VorlekProviderRateLimitedErrorPROVIDER_RATE_LIMITEDtrueProvider throttled the upstream call; retry with backoff.
VorlekProviderUnavailableErrorPROVIDER_UNAVAILABLEtrueProvider is temporarily unavailable.
VorlekProviderFailedErrorPROVIDER_FAILEDfalseProvider reached a terminal failure; inspect provider detail.
VorlekInternalErrorINTERNAL_ERRORtrueUnexpected Vorlek-side failure; report the request id.
VorlekConnectionDecryptFailedErrorCONNECTION_DECRYPT_FAILEDfalseStored provider credential cannot be decrypted; reconnect provider.
VorlekNetworkErrorNETWORK_ERRORtrueLocal network, DNS, TLS, timeout, or transport failure.
from vorlek import VorlekClientError, VorlekRateLimitedError, is_retryable_error

try:
    result = client.contact.upsert(provider="sendgrid", email="jamie@example.com")
except VorlekRateLimitedError as err:
    schedule_retry(after_seconds=err.retry_after)
except VorlekClientError as err:
    fix_request_or_connection(err.code)
except Exception as err:
    if is_retryable_error(err):
        retry_with_backoff()

Examples

Test mode

vk_test_* keys run the same SDK methods against deterministic API fixtures. Use them for CI, retry-path tests, and agent evals; the full behavior is documented at vorlek.dev/docs/test-mode.

Cross-SDK parity

The TypeScript SDK is @vorlek/sdk. Both SDKs expose the same {data, meta} contract and six tool namespaces through registry-installed packages.