helpvox — Python SDK for the HelpVox app platform
Build apps that plug into HelpVox: install via OAuth, receive events, give the AI agent tools, embed a dashboard page and call the REST API. Python 3.9+, no dependencies.
pip install helpvox
The wire contract this SDK follows is in docs/apps/platform-contract.md.
You need your app's client_id (hvapp_…) and client_secret (hvs_…). The secret is the HMAC key
for everything HelpVox sends you — keep it in one environment variable.
Install (OAuth)
Send the merchant to the consent screen, then exchange the code on your callback:
import helpvox
url = helpvox.install_url("loyalty-points", redirect_url="https://app.example/oauth/callback", state=csrf)
# GET /oauth/callback?code=hvic_…&installation_id=…&state=…
token = helpvox.exchange_code(CLIENT_ID, CLIENT_SECRET, code)
# {"access_token": "hva_…", "token_type": "bearer", "scope": "…", "installation_id": "…", "vendor_id": "…"}
store(token["installation_id"], token["access_token"])
# Your agent tools stay off until you say you are ready:
helpvox.Client(token["access_token"]).ready()
Codes live 10 minutes and work once — exchanging the same code twice revokes the installation.
Failures raise helpvox.ApiError (e.code == "invalid_grant").
Events
Always verify against the raw request body.
from helpvox import parse_event, SignatureError
try:
event = parse_event(CLIENT_SECRET, raw_body, request_headers)
except SignatureError:
return 401
if already_processed(event.id): # the same id is sent on every retry
return 200
if event.type == "order.status_changed":
...
elif event.type == "app.uninstalled":
delete_data(event.installation_id)
Event has id, type, created_at, vendor_id, installation_id, data, is_test. Header lookup is
case-insensitive and accepts any dict-like headers object. Answer 2xx within 10 seconds.
Send yourself a sample with client.send_test_event("order.created") (event.is_test is True).
Agent tools
Your handler gets (input, context) and returns plain text for the agent (cut to 2000 characters),
or raises ToolError with a short reason the agent can tell the customer.
from helpvox import ToolContext, ToolError, handle_tool_call
def get_balance(input: dict, ctx: ToolContext) -> str:
points = lookup(ctx.installation_id, input["phone"])
if points is None:
raise ToolError("There is no loyalty account for this phone number.")
return f"The customer has {points} points."
handle_tool_call returns (status, json_body): 401 for a bad signature, 400 for a malformed
body, otherwise 200 with {"result": …} or {"error": …}. Other exceptions propagate; HelpVox
then continues without the tool. ToolContext has tool, installation_id, vendor_id, channel, conversation_id, language, country, caller_phone.
Flask:
from flask import Flask, jsonify, request
app = Flask(__name__)
@app.post("/tools/balance")
def balance():
status, body = handle_tool_call(CLIENT_SECRET, request.get_data(), request.headers, get_balance)
return jsonify(body), status
FastAPI:
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
@app.post("/tools/balance")
async def balance(request: Request):
status, body = handle_tool_call(CLIENT_SECRET, await request.body(), request.headers, get_balance)
return JSONResponse(body, status_code=status)
Phone tools have 1500 ms by default, so keep the handler fast (if it blocks, run it in a thread pool
in async frameworks). Events work the same way: parse_event(CLIENT_SECRET, request.get_data(), request.headers).
Dashboard page
If your manifest sets ui.settings_page, HelpVox embeds it with ?installation_id=…&session=<JWT>.
The token lives 60 seconds — verify it, then start your own session:
from helpvox import verify_session_token
claims = verify_session_token(CLIENT_SECRET, request.args["session"], CLIENT_ID)
if claims is None:
abort(401)
login(installation_id=claims["installation_id"], user=claims["sub"], lang=claims["lang"])
sign_session_token(secret, claims) issues one the same way HelpVox does, for your tests.
REST API
from helpvox import ApiError, Client
client = Client(access_token)
client.app() # installation, vendor, scopes, ready, capabilities
orders = client.orders(status="new", limit=50)
client.order(order_id)
client.update_order(order_id, {"note": "Gift wrap"}) # orders:write
client.products(); client.calls(); client.call(call_id)
client.callbacks(); client.appointments()
d = client.create_discount(conversation_id, 12.00, "EUR", "Loyalty points", reference="ledger-42")
client.discount(d["id"]); client.cancel_discount(d["id"])
# anything else
client.request("GET", "/api/v1/categories")
try:
client.calls()
except ApiError as e:
print(e.status, e.code, e.message, e.scope) # 403 insufficient_scope … conversations:read
Customer fields are removed from responses without customers:read; transcripts without
transcripts:read.
For tests, pass transport= — a callable (method, url, headers, body_bytes) -> (status, body_bytes) —
to Client or exchange_code. Signatures: helpvox.sign(secret, body) / helpvox.verify(secret, body, header).
Development
cd sdk/python
python3 -m unittest discover -s tests
Metadata
Release files for helpvox 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| helpvox-0.2.0.tar.gz | 15.6 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| helpvox-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 31.5 kB
Release files / helpvox-0.2.0.tar.gz
| Download URL | helpvox-0.2.0.tar.gz |
|---|---|
| Size | 15.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a9d8d604642f2032a0dc90c9cde6293552dfc8465af38b766da040cfcd033f97
|
|
BLAKE2b-256 checksum How to use checksums |
90a43e745cc10e9c194d25c17212904b2ab585b3511b6cf8218dcecac735f4d0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logRelease files / helpvox-0.2.0-py3-none-any.whl
| Download URL | helpvox-0.2.0-py3-none-any.whl |
|---|---|
| Size | 15.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ed5841afdb7b93ef2f0da70122f32817a0b0aa464129f0fc511e6ef9448787c2
|
|
BLAKE2b-256 checksum How to use checksums |
3d147820e0a32798cda7c138c1491a4deb1556616cef10746aedf21212b44d67
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logSecurity for helpvox 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for helpvox-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.