# Adeva Pro Public API > First-class REST API for Adeva Pro client organisations to raise debts, read > their owned data, update branding, and receive lifecycle events as webhooks. > Built for both humans and autonomous agents. ## Hosts - Production: https://api.adevapro.com.au — live keys only (`ak_live_…`) - Sandbox: https://sandbox.adevapro.com.au — test keys only (`ak_test_…`), data resets every 7 days of inactivity, Stripe in test mode, outbound notifications disabled. Modes never cross. Presenting a key of the wrong mode to a host returns `401 unauthenticated` before any business logic runs. ## Discoverability - OpenAPI 3.1.1 spec: `/openapi/v1.json` - Interactive reference (Scalar): `/reference` - Swagger UI: `/swagger` - Human-readable guides: `/docs/` - Liveness probe: `/healthz` → `{ ok: true, mode: "sandbox" | "production" }` ## Authentication Every `/v1/*` request requires `Authorization: Bearer `. - Live keys are minted from the Adeva Pro client console → Developer → API keys. - Test keys are minted from the sandbox dashboard (self-serve via B2C sign-up). - Full secret is shown once at creation. We store only the SHA-256 hash + 16-char display prefix. ## Scopes (least-privilege) ``` debts:read · debts:write · debts:documents:write debtors:read · debtors:write payments:read · transactions:read branding:read · branding:write webhooks:read · webhooks:write ``` A key also carries a coarse `readOnly` flag that rejects all `:write` scopes regardless of what the key was granted. ## Endpoints (v1) ### Debts - `GET /v1/debts` — list, cursor-paginated. Query: `cursor`, `limit` (max 200). - `GET /v1/debts/{id}` — get by id. 404 on cross-tenant (no existence leak). - `POST /v1/debts` — create. Either `debtorId` OR inline (`debtorReferenceId` + names + email/phone). Fires `debt.created`. - `PATCH /v1/debts/{id}` — update mutable fields. Fires `debt.updated`. ### Debtors - `GET /v1/debtors`, `GET /v1/debtors/{id}`. ### Payments + Transactions - `GET /v1/payments` — inbound + succeeded only. Filter `debtId`. - `GET /v1/transactions` — full ledger. Filter `debtId`. ### Organization - `GET /v1/organization` — calling org (any valid bearer). - `GET /v1/organization/branding`, `PATCH /v1/organization/branding`. ### Webhooks - `GET /v1/webhooks` — current config + secret prefix. - `PUT /v1/webhooks` — set URL / `rotateSecret: true` / `clear: true`. - `POST /v1/webhooks/test` — synthetic `test.ping`. ## Idempotency Set `Idempotency-Key: ` on any `POST`/`PUT`/`PATCH`/`DELETE`. Same key + same body within 24h returns the saved response with `Idempotent-Replayed: true`. Same key + different body returns `409 idempotency_conflict`. ## Errors (RFC 7807) ```json { "type": "https://adevapro.com.au/errors/", "title": "", "status": , "detail": "..." } ``` Common codes: `unauthenticated`, `forbidden`, `read_only_key`, `invalid_request`, `missing_debtor`, `debtor_not_found`, `not_found`, `idempotency_conflict`, `idempotency_in_progress`, `no_webhook_configured`, `invalid_color`, `invalid_url`, `invalid_principal`, `missing_external_account_id`. Status matrix per authenticated op: `200/201/204` · `400` · `401` · `403` · `404` · `409` · `429` · `5xx`. ## Rate limits Defaults — Live: 1000/h, 20000/d. Test: 3600/h, 50000/d. On 429: `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`. ## Webhooks (outbound) - POSTed as JSON envelope: `{ id, type, createdAt, data }`. - Signed: `Adeva-Signature: t=,v1=` where `v1 = HMAC_SHA256(secret, ".")`. - Compare with constant-time equality. Reject if `|now - t| > 300s`. - Retries: 1m, 5m, 30m, 2h, 12h, 24h (6 attempts then `Failed`). - At-least-once. Dedupe on the envelope `id` or `Adeva-Delivery-Id` header. Event types: `debt.created`, `debt.updated`, `debt.accepted`, `debt.disputed`, `payment.received`, `plan.activated`, `plan.defaulted`, `test.ping`. ## Versioning - Stable URL prefix: `/v1`. Breaking changes go to `/v2`. - Additive changes (new fields, new endpoints, new events) ship without version bumps. - Deprecations flagged via `Deprecation` header (RFC 9745) with a sunset date, minimum 6 months notice before removal. ## Conventions - Times are ISO-8601 UTC. - Money is decimal, currency on the same object (ISO-4217). - Pagination is cursor-based; opaque base64. `nextCursor` is null at end. - Tenant scope is implicit in your key — every query is filtered by your `OrganizationId`. ## Headers Adeva sends on every response - `X-Request-Id` — quote this in any support ticket; it ties back to our Seq logs. - `Idempotent-Replayed: true` — only present when this response was replayed from the idempotency store.