PSA/ee/docs/plans/2026-06-08-hudu-integration/hudu-api-reference.md
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

58 lines
3.5 KiB
Markdown

# Hudu API — Implementation Reference
Distilled from the local Hudu skills for executors of this plan (the skills live in gitignored `.claude/`; this committed copy is the source of truth for implementation). Pull-only; all calls are GET.
## Auth & base URL
- Header auth: `x-api-key: <API_KEY>` + `Content-Type: application/json`.
- Base URL is per-instance: `https://<instance>/api/v1/<resource>` (Hudu Cloud `*.huducloud.com` or self-hosted). Store `hudu_api_key` + `hudu_base_url` per tenant in the secret provider (Vault); never return the key to the client; never log it.
- Validate a connection with `GET /api/v1/companies?page=1`. Probe password capability with a `GET /api/v1/asset_passwords?page=1` — a `403` means the key lacks password access (surface that, don't crash).
## Endpoints used (Phase 1)
| Purpose | Endpoint | Key params |
| --- | --- | --- |
| Companies | `GET /api/v1/companies` | `?page=`, `?id_in_integration=`, `?name=`, `?search=` |
| Assets (by company) | `GET /api/v1/assets` | `?company_id=`, `?page=`, `?archived=false` |
| Articles (by company) | `GET /api/v1/articles` | `?company_id=`, `?page=` |
| Passwords (by company) | `GET /api/v1/asset_passwords` | `?company_id=`, `?page=` |
| Single password (reveal) | `GET /api/v1/asset_passwords/{id}` | — |
| Deep-link by PSA id | `GET /api/v1/companies/jump` | `?integration_id=&integration_slug=&integration_type=company` |
## Naming traps (UI label → API resource)
`Passwords``asset_passwords` · `Processes``procedures` · `Knowledge Base Article``articles` · `Company``companies`. Always use the API name.
## Pagination
Page-based, **fixed 25 items/page**: `?page=1,2,…`. A page returning `< 25` items (or empty) is the last page. Loop until then. Never bulk-enumerate across all clients on a page view — fetch per mapped company only.
## Rate limiting
**300 requests/minute.** On `429`, back off using the `Retry-After` header (+ jitter) and retry with a capped attempt count; on transient `5xx`, exponential backoff. No consumer webhooks / change-feed exist — polling only.
## Error mapping
| Code | Meaning | Handling |
| --- | --- | --- |
| 401 | bad/expired key | typed "invalid key" state |
| 403 | key lacks password permission | typed "no password access" state (not an error) |
| 404 | bad base URL or id | typed "not found" |
| 422 | validation | n/a for pull |
| 429 | rate limited | backoff + retry |
| 5xx | server | retry w/ backoff |
## Company ↔ Client matching
The PSA is the source of truth for companies; Hudu stamps imported companies with `id_in_integration` (+ `integration_slug`). Auto-suggest a Hudu company → Alga client by: (1) `id_in_integration` exact-equals an Alga `client_id`, else (2) exact name, else (3) fuzzy name (lower confidence). Admin confirms/overrides. One Hudu company ↔ one Alga client.
## Response shapes (illustrative)
- Single: `{ "company": { "id", "name", "id_in_integration", "url", … } }`
- Collection: `{ "companies": [ … ] }` (resource name is the plural key)
- `asset_password`: `{ id, company_id, name, username, password, url, password_folder_name, … }`**the `password` field is plaintext.**
## SECURITY (hard constraint)
The reveal endpoint returns the credential **plaintext**. Phase 1: reveal on demand via a single live GET, return the value transiently to the browser (masked, reveal-on-click), **audit every reveal**, and **never persist the value** — not a DB column, not Vault, not a cache — and never log it. Lists carry metadata only (name/username/url/id), never the value.