Skip to main content

Python SDK for the HAX (Human Approval eXchange) API

Project description

HAX Python SDK

Python client for the HAX (Human Approval eXchange) API. Enables agents and automated systems to programmatically collect human input.

Installation

The Python SDK is not yet published to PyPI. Install from source:

pip install -e sdks/python  # from the repo root
# or
pip install -e .  # from sdks/python/

Quick Start

from hax import HaxClient

client = HaxClient(
    api_key="hax_live_...",
    base_url="http://localhost:3000/api/v1",
)

request = client.create_request(
    type="text-approval-v1",
    payload={"text": "Deploy main to prod?", "approveLabel": "Ship it", "denyLabel": "Hold"},
    webhook_url="https://myapp.com/webhook",
)

print("Share with approver:", request.url)

# Poll until completed/expired/cancelled
request = client.wait_for_response(request.id, timeout=300)
if request.is_completed:
    print("Decision:", request.response.get("decision"))

Features

  • Pydantic models: Typed request/response models with validation
  • FormBuilder: Fluent API for building typed forms with runtime type inference
  • E2E encryption: RSA-OAEP + AES-GCM hybrid encryption for sensitive responses
  • Webhook verification: HMAC-SHA256 signature verification
  • Delivery: Send requests via email or SMS
  • Polling: Built-in wait_for_response with configurable timeout
  • Error handling: Typed exception hierarchy

Request Methods

# Create a request
request = client.create_request(
    type="text-approval-v1",
    payload={"text": "Approve this action?"},
    title="Optional title",
    description="Optional description",
    webhook_url="https://myapp.com/webhook",
    expires_in_seconds=3600,
    metadata={"pr_number": 123},
)

# Send via email
request = client.request_via_email(
    type="confirm-action-v1",
    payload={"title": "Approve?", "confirmPhrase": "YES"},
    to_email="approver@example.com",
    subject="Approval Required",
)

# Send via SMS
request = client.request_via_sms(
    type="text-approval-v1",
    payload={"text": "Approve?"},
    to_phone="+15551234567",
)

# Get a request by ID
request = client.get_request("req_123")

# List recent requests
requests = client.list_requests()

# Cancel a pending request
cancelled = client.cancel_request("req_123")

# Submit a response (for testing)
completed = client.submit_response("req_123", {"decision": "approve"})

# Wait for completion with timeout
result = client.wait_for_response("req_123", poll_interval=2.0, timeout=60)

# List available template types
types = client.list_types()

Status Helpers

if request.is_pending:
    print("Waiting for response...")
if request.is_completed:
    print("Response:", request.response)
if request.is_expired:
    print("Request expired")
if request.is_cancelled:
    print("Request was cancelled")

FormBuilder

Build typed forms with a fluent API:

from hax import HaxClient, FormBuilder

client = HaxClient(api_key="hax_live_...")

form = (FormBuilder()
    .title("Event Registration")
    .input("name", label="Full Name", required=True)
    .input("email", label="Email", variant="email", required=True)
    .number("age", label="Age", min=0, max=120)
    .checkbox("newsletter", checkbox_label="Subscribe to newsletter"))

handle = client.create_form_request(form,
    webhook_url="https://myapp.com/webhook")

print(f"Form URL: {handle.url}")

# Wait for typed response
response = handle.wait_for_response(timeout=300)
print(response.values.name)       # str
print(response.values.email)      # str
print(response.values.age)        # float
print(response.values.newsletter) # bool

Available Field Types

Method Output Type Description
.input(id) str Text input (variants: text, email, url, tel)
.textarea(id) str Multi-line text input
.select(id, options=...) str Dropdown select
.radio_group(id, options=...) str Radio button group
.date(id) str Date picker (ISO format)
.number(id) float Numeric input
.slider(id, min=, max=) float Slider control
.checkbox(id) bool Single checkbox
.switch(id) bool Toggle switch
.checkbox_group(id, options=...) list[str] Multi-select checkboxes
.hidden(id, value) type(value) Hidden field

Webhooks

Verify and parse webhook events:

from hax import verify_signature, parse_event

# In your webhook handler
def handle_webhook(request):
    # Verify signature
    is_valid = verify_signature(
        payload=request.body,
        signature=request.headers["X-Hax-Signature"],
        secret="whsec_...",
    )
    if not is_valid:
        return 400, "Invalid signature"

    # Parse the event
    event = parse_event(request.body)

    if event.event_type == "completed":
        print(f"Request {event.request_id} completed!")
        print(f"Response: {event.response}")
    elif event.event_type == "expired":
        print(f"Request {event.request_id} expired")

    return 200, "OK"

Event Types

  • request.sent — Notification was delivered (email/SMS)
  • request.opened — Human opened the request link
  • request.completed — Human submitted a response
  • request.expired — Request expired without action

Encryption

For sensitive response data, use end-to-end encryption:

from hax import HaxClient

# Passphrase-based (automatic encrypt/decrypt)
client = HaxClient(
    api_key="hax_live_...",
    encryption_key="my-secret-passphrase",
)

# Public key is automatically sent with requests
request = client.create_request(
    type="text-approval-v1",
    payload={"text": "Approve this sensitive action?"},
)

# Response is automatically decrypted when retrieved
completed = client.get_request(request.id)
print(completed.response)  # Decrypted plaintext

Manual Decryption

from hax import generate_key_pair, decrypt_response, is_encrypted_response

public_key, private_key = generate_key_pair("my-secret")

# Use public_key when creating the client
client = HaxClient(api_key="...", public_key=public_key)

# Later, manually decrypt
request = client.get_request("req_123")
if is_encrypted_response(request.response):
    decrypted = decrypt_response(request.response["_encrypted"], private_key)

Error Handling

from hax import (
    HaxError,             # Base error
    AuthenticationError,  # Invalid API key (401)
    ValidationError,      # Invalid request data (400/422)
    NotFoundError,        # Resource not found (404)
    RateLimitError,       # Too many requests (429)
    ServerError,          # Server error (500+)
    DecryptionError,      # Decryption failure
)

try:
    request = client.create_request(...)
except AuthenticationError:
    print("Check your API key")
except ValidationError as e:
    print(f"Invalid request: {e}")
except RateLimitError:
    print("Rate limited, try again later")
except HaxError as e:
    print(f"API error: {e}")

Template Types

Template Description
text-approval-v1 Show text and collect an approve/deny decision
confirm-action-v1 Require typing a specific phrase to confirm a destructive action
collect-email-v1 Prompt the user for an email address
form-builder Advanced forms with field types, layouts, validation, and conditional logic
multi-choice-selection-v1 Single or multiple selection from customizable option cards
code-changes-v1 GitHub-style diff view with inline line comments
rich-text-editor-v1 Markdown-formatted text editing for documents and reports
file-upload-v1 Collect files (documents, images, CSVs) from users
signature-capture-v1 Capture e-signatures with optional signer name and legal text
data-table-review-v1 Review, select, or edit tabular data
scheduling-picker-v1 Date/time slot selection with optional recurring schedules
multi-step-wizard-v1 Sequential steps with navigation and progress indicator
side-by-side-comparison-v1 Compare two versions with diff highlighting
terminal-output-v1 Display command output/logs with approve-to-continue

Notes

  • Auth is API key only. Provide the key via HaxClient(api_key=...); Clerk/session auth is not required for API access.
  • API responses wrap resources (e.g., {"request": {...}}); the SDK unwraps this automatically.
  • Template payloads and responses are flexible; consult the template configs for the fields each template expects/returns.
  • The client supports context manager usage: with HaxClient(...) as client:

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

hax_sdk-0.2.4rc7.tar.gz (30.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hax_sdk-0.2.4rc7-py3-none-any.whl (23.4 kB view details)

Uploaded Python 3

File details

Details for the file hax_sdk-0.2.4rc7.tar.gz.

File metadata

  • Download URL: hax_sdk-0.2.4rc7.tar.gz
  • Upload date:
  • Size: 30.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for hax_sdk-0.2.4rc7.tar.gz
Algorithm Hash digest
SHA256 874dd1b407338d72ad2d62042a4522e2332010d32b2ae3fd7ab29faff4feea28
MD5 43339b1c87bb67606f0292e46b3d97a9
BLAKE2b-256 04e5397324904cebd1d4b0ca5754456872a6688778be3f2ce8c520d97b14a7a0

See more details on using hashes here.

File details

Details for the file hax_sdk-0.2.4rc7-py3-none-any.whl.

File metadata

  • Download URL: hax_sdk-0.2.4rc7-py3-none-any.whl
  • Upload date:
  • Size: 23.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for hax_sdk-0.2.4rc7-py3-none-any.whl
Algorithm Hash digest
SHA256 00a44b669d1071d957d9c3781fd9f215e13a4589e7e20985417c1321afb95450
MD5 7e83d006b6a6eb3325a1cf0e32abff12
BLAKE2b-256 31cfb72c7e8396a618b1780b44506a955d26ae2bbb0d88da9488cc4981ec0216

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page