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.4.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.4-py3-none-any.whl (23.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: hax_sdk-0.2.4.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.4.tar.gz
Algorithm Hash digest
SHA256 6a26234b95a421dbf92c0c445e58351a1794229b27a2a2077b8270b6baaa5104
MD5 c9994af1252183f4d528f06ac6984a22
BLAKE2b-256 f25d4c09b4ff40baf42f6bfe37f7424e833c5b2b24ce088c60f98b6515b663da

See more details on using hashes here.

File details

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

File metadata

  • Download URL: hax_sdk-0.2.4-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.4-py3-none-any.whl
Algorithm Hash digest
SHA256 8a4016e1e215b6dd35391a133e4b182bcaf2cfb90b01ed133727859810718999
MD5 5b44506bf4a7e9c9f3ffe9575e0754a2
BLAKE2b-256 44cd5504e23193c817dcebafea5edc522c29082a6fac4fd42b4e6e0528110c57

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