Developers
An ERP you can build against
Everything the product does rides on the same open surface: a PAT-only REST API, discoverable per-tenant models, HMAC-signed webhooks and a test zone that deletes itself.
Get started in three steps
No SDK required — a key, a base URL and the interactive reference are all a first call needs.
Mint a key
Console → API keys. Pick Test to get an sk_test_ key bound to a sandbox twin of your organization — or Live for production.
Authenticate
Send the key as a Bearer header: Authorization: Bearer sk_… Scopes can bound a key to exactly the entities and verbs it needs.
Open the reference
The Swagger UI documents every route, filter and lookup — including discovery and webhooks — straight from the live schema.
Base URL: https://updo.pro/api/public/v1 · Interactive API reference
A Python SDK, and a command line
`updo-sdk` wraps the public API: same token, same contract, in Python objects. It reads `UPDO_API_TOKEN` from the environment — so an `sk_test_` key points it at your test zone and an `sk_` key at your real data, without touching a line of code.
pip install updo-sdk pip install "updo-sdk[cli]" # + the updo command
Synchronous
from updo import UpdoClient
# Or: export UPDO_API_TOKEN=sk_test_... (UPDO_BASE_URL to point elsewhere)
with UpdoClient(token="sk_test_...") as client:
print(client.me().tenant_slug)
products = client.entity("product")
for p in products.iterate(where={"status": "active"}):
print(p["sku"])Asynchronous
from updo import AsyncUpdoClient
async with AsyncUpdoClient(token="sk_test_...") as client:
products = await client.entity("product")
async for p in products.iterate(where={"status": "active"}):
print(p["sku"])What the client raises
AuthenticationErrorToken missing, revoked or expired (401).
PermissionDeniedThe token is valid but the access decision says no, or its scopes do not cover the gesture (403).
ValidationErrorThe server refused the value — required field, undeclared choice, business rule (400).
NotFoundErrorThe record does not exist, or not in this organization (404).
ApprovalRequiredNOT an error: the transition is parked awaiting approval (202). The request id is carried on the exception.
From the command line
Installed with the `[cli]` extra. Handy to explore an entity, pull an export, or generate typed models from YOUR organization's data model.
updo get product --where status=active --limit 20 updo export product --format xlsx --out products.xlsx updo codegen --out models.py
Package, releases and full reference: pypi.org/project/updo-sdk
Discover the model
Entities are defined per tenant, so the API tells you what exists: GET /data/ lists every entity your key may read, each with its schema URL. A denied entity is simply absent — the index is never an oracle.
Read and write records
Generic record envelopes, data__<field> filters with lookups, free-text search, aggregate and pivot for BI, CSV/XLSX export — all bounded by the same access decisions as the product itself.
curl https://updo.pro/api/public/v1/data/ \ -H "Authorization: Bearer sk_test_..."
Webhooks, managed by the API
Subscribe to any business event — invoice.paid, ticket.created, your own emitted events — straight from the API. Every delivery is journaled per attempt, with the HTTP code, duration and next retry readable through the same key.
curl -X POST https://updo.pro/api/public/v1/webhooks/ \
-H "Authorization: Bearer sk_..." \
-H "Content-Type: application/json" \
-d '{
"slug": "invoice-paid",
"event_pattern": "invoice.paid",
"config": {
"url": "https://example.com/hooks/updo",
"secret": "whsec_..."
}
}'The signature contract
X-Qlaris-Signaturesha256=<hex> — HMAC-SHA256 of the exact request body bytes, keyed with your subscription secret.
X-Qlaris-EventThe event name that fired the delivery.
X-Qlaris-DeliveryStable across one event's retries — deduplicate on it.
X-Qlaris-TimestampUnix seconds at send time. Reject anything older than 5 minutes to block replays.
import hashlib, hmac
def verify(secret: str, raw_body: bytes, signature: str) -> bool:
digest = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(f"sha256={digest}", signature)A test zone that cleans up after itself
An sk_test_ key opens a sandbox twin of your organization: same modules, same model, seeded data — and a hard boundary with production.
A real twin
The sandbox is provisioned by the same installer as production — what works there works live.
Harmless by construction
Documents render with a NOT-TO-USE watermark and no email ever actually leaves the sandbox.
Least privilege
Scope a key to data:<entity>:read, analytics:*, webhooks:write… Empty scopes mean the full rights of the key's roles.
Evaluate the platform on your own operations
Create your workspace in minutes, with your profession's applications already in place. No credit card.