REST
JSON over HTTPS. Every path on this page sits under this base.
https://api.veraxus.ai/v1
Developer docs
One API for the cloud systems your customers sign into through a browser. Read what the signed-in user can see, write changes back with their approval, and export what they can download.
REST for your software. MCP for agents.
Authenticate with a pk_live_ key, or OAuth if you are ChatGPT, Claude, Cursor, or Grok.
Spec is public. Calls are not.
How it fits together
Every call runs on one path: your tenant, one of your customers, and that customer’s saved login to one system. Connections, credentials and usage stay on that path. One customer’s session is never shared with another.
| Layer | Who | In the API |
|---|---|---|
| tenant | Your company | Owns your keys and webhooks. GET /v1/me names it. |
| organization | Your customer | GET /v1/organizations lists them. POST /v1/organizations creates one or returns the existing one. |
| connection | One saved login to one system | PUT /v1/credentials saves it. POST /v1/portal/connect signs in. GET /v1/portal-logins reports its status. |
Calls that act on one customer take organization_id. A tenant key must send it. A key pinned to one organization may leave it out.
GET /v1/organizations is cursor-paginated: 50 per page by default, up to 100 with limit.
Filter by name with q and pass next_cursor back as cursor.
POST /v1/organizations/import adds up to 500 at once.
Base URLs
JSON over HTTPS. Every path on this page sits under this base.
https://api.veraxus.ai/v1
Agents POST here. A browser tab is not a client. Paste the URL into ChatGPT, Claude, Cursor, or Grok.
https://api.veraxus.ai/mcp
grok mcp add --transport http veraxus https://api.veraxus.ai/mcp
Authenticate
Authorization: Bearer pk_live_… Minted in the console. Shown once.Authorization: Bearer vat_live_…. Unauthenticated MCP returns 401 with OAuth discovery. The user signs in on the console and pins one organization.GET /v1/me returns the key’s org_id (null for a tenant key), its scopes, and its caller_id.403 insufficient_scope names the scope the key lacks in error.required_scope. Webhooks need a tenant key with the vault scope. Attested approvals need portal_login_mutation.Connect a customer’s system
Your product collects the username and password and sends them to Veraxus. If the system asks for a verification code, your customer pastes it into your screen. After that, every read reuses the saved login and never posts the password.
| Step | REST | MCP |
|---|---|---|
| Resume an interrupted run | GET /v1/portal/mfa, then GET /v1/portal-logins | list_portal_mfa_challenges, then list_portal_logins |
| Save the login | PUT /v1/credentials | store_credentials |
| Sign in once | POST /v1/portal/connect | connect_portal_login |
Send the code if needs_code |
POST /v1/portal/mfa | complete_portal_mfa |
| Confirm it is saved | GET /v1/portal-logins | list_portal_logins |
PUT /v1/credentials stores the username, the password and an optional authenticator seed (totp_seed)
for one organization and one system. The password and seed are write-only: vaulted, never returned.
GET /v1/credentials lists saved logins with secrets redacted.
Saving does not sign in. Call Connect next. A new password for the same username saves normally.
Replacing the username on a login that already has a password returns 409 portal_login_identity_change_not_supported.
POST /v1/portal/connect signs in with the vaulted login. The body never carries the password.
Send an Idempotency-Key of 16 to 128 characters. One key runs one Connect.
A replay within 24 hours returns the stored receipt with replayed: true.
The same key with a different body is 409 idempotency_mismatch. One still running is 409 attempt_in_progress.
Saving a login and Connect both need an approval: an approved grant id in the X-Veraxus-Hitl-Grant header,
or a key with the portal_login_mutation scope plus an attestation object
(fields under Read, write, export). Without either, the call returns 403 hitl_required.
Sending a code with POST /v1/portal/mfa needs neither. It needs the code.
If the system uses an authenticator app, save its seed as totp_seed so Veraxus can compute the code.
When an organization has a code address (code_email_address), Veraxus can receive emailed codes there.
Do not point a system account at it until code_email_receiving is true.
Otherwise your customer sends the code through POST /v1/portal/mfa.
not_connected or expired.needs_code.ready, show Signed in. Do not call Connect again.GET /v1/portal/mfa first. A pending hold gets the code; do not Connect again. Then read GET /v1/portal-logins.GET /v1/portal-logins/snapshot never contacts the system. Its status turns status_unknown once the last reading is ten minutes old.| Status | What you do |
|---|---|
| not_connected | Nothing is signed in. Save the login, then Connect once. |
| needs_code | Show a code field and send the code with POST /v1/portal/mfa. Do not sign in again. |
| confirmation_pending | The system already has the code. Hide the code field. Observe with POST /v1/portal/mfa and observe_only: true, no code. Never send the code again. |
| ready | Signed in. Calls reuse the saved login. Do not Connect. |
| reconnecting | On GET /v1/portal-logins rows only. Veraxus is signing back in by itself. Poll; do not Connect. |
| expired | The system ended the session. With a person at the desk, Connect once. |
| credentials_invalid | The system refused the saved login. Ask your customer to update it with PUT /v1/credentials, then Connect. |
| locked | The account is locked on the system. Do not retry Connect. |
| portal_outage | The login is saved. The system is not answering. Retry later. Do not ask for the password. |
| quarantined | The login is saved. The last sign-in was blocked. Retry later. Do not ask for the password. |
| vault_error | Veraxus could not read the vault. Retry later. Do not ask for the password. |
| status_unknown | Not verified yet. Do not show it as signed in. Read the status again. |
connected is a legacy alias of ready. New code should switch on ready.
POST https://api.veraxus.ai/v1/portal/connect
Authorization: Bearer pk_live_…
Idempotency-Key: ••••••••••••••••
// body: organization_id and the system id. See the spec.
// 200
{
"ok": true,
"status": "needs_code",
"message": "Enter the one-time code.",
"complete_with": "complete_portal_mfa",
"generation": 1,
"client_reference": null
}
POST https://api.veraxus.ai/v1/portal/mfa
Authorization: Bearer pk_live_…
// body: organization_id, the system id and the code
// 200
{
"ok": true,
"status": "ready",
"message": "Login saved."
}
POST https://api.veraxus.ai/v1/portal/connect
Authorization: Bearer pk_live_…
// 503, with a Retry-After header
{
"error": {
"code": "portal_outage",
"message": "…",
"retryable": true,
"retry_after": …
}
}
Request bodies and every field are in the spec.
Read, write, export
A connected system lists what it can do, each operation marked read or write. Reads and writes go through the same call. Today this interface serves connected cloud practice-management software.
| Step | REST | What comes back |
|---|---|---|
| List systems | GET /v1/pms/vendors | Each system’s id, name, address_example and second_factor. |
| Check connections | GET /v1/pms/connections | Status per system for one organization, with last_authenticated_at, last_served_at and last_failure_at. Never signs in. |
| List operations | GET /v1/pms/{vendor}/operations | Each operation’s name, kind (read or write), summary, input, and input_schema when available. |
| Run one | POST /v1/pms/{vendor}/execute/{operation} | status, operation, kind, provenance, projected, raw, http_status, hops, realm, client_reference. |
Connect these systems with the same POST /v1/portal/connect, naming the system by its id from GET /v1/pms/vendors.
There is no separate connect route. PUT /v1/credentials also takes portal_url, your customer’s own address on that system.
{ organization_id, input }. Validate input against the operation’s input_schema when it has one.409 not_connected means Connect first.projected is the normalized result. It is null when that operation’s shape has not been captured yet; use raw, which holds what the system returned.X-Veraxus-Hitl-Grant, or portal_login_mutation plus an attestation.Idempotency-Key of 16 to 128 characters. One key runs one write. A replay returns the stored receipt.502 unknown_outcome means the result is unknown: reconcile with a read before acting again.422 means the system rejected the write. Inspect raw and reconcile before changing the approved change.An export is a read. It runs under the same saved login, organization isolation and audit as any other read operation.
503 means the system or the connection is unavailable right now. The saved login stays in place.
Retry later. Do not ask for the password.
| attestation field | What it records |
|---|---|
| actor_user_id | Required. Your app’s id for the person who entered or approved the action, up to 200 characters. Not a Veraxus console user. |
| consent_session_id | Required. Your consent session or screen id that tied the action to that person, up to 200 characters. |
| consented_at | Required. ISO-8601. Within the last 24 hours and no more than 5 minutes in the future. |
| consent_kind | Optional. session (default): a person approved in this session. standing_policy, for writes only: your customer turned on a standing write policy. Send your policy record as consent_session_id. It is exempt from the 24-hour window. |
GET https://api.veraxus.ai/v1/pms/{vendor}/operations
Authorization: Bearer pk_live_…
// 200
{
"operations": [{
"name": "••••",
"kind": "read",
"summary": "••••",
"input": { … },
"input_schema": { … }
}]
}
POST https://api.veraxus.ai/v1/pms/{vendor}/execute/{operation}
Authorization: Bearer pk_live_…
{
"organization_id": "org_…",
"input": { … }
}
// 200
{
"status": "ok",
"operation": "••••",
"kind": "read",
"projected": { … },
"raw": { … },
"http_status": 200,
"hops": [ … ],
"realm": "••••",
"client_reference": null
}
POST https://api.veraxus.ai/v1/pms/{vendor}/execute/{operation}
Authorization: Bearer pk_live_…
Idempotency-Key: ••••••••••••••••
{
"organization_id": "org_…",
"input": { … },
"attestation": {
"actor_user_id": "••••",
"consent_session_id": "••••",
"consented_at": "2026-10-14T15:04:05Z"
}
}
// 502 unknown_outcome: do not resend. Read first.
Webhooks
Register an HTTPS URL with POST /v1/webhooks, using a tenant key with the vault scope.
Veraxus POSTs JSON signed with HMAC-SHA256. The signing secret (whsec_…) is returned once, on create and on rotate-secret.
Veraxus-Event, Veraxus-Delivery, and Veraxus-Signature: t=<unix>,v1=<hex>, an HMAC over ${t}.${body}.v1 values. Accept any that matches the secret you hold.webhook.deactivated.payload.id.organization_id for a tenant-wide default. Set it to override the default for one organization. Scope is fixed at creation.
Events include portal_login.status_changed (Connect, code and session-health changes),
portal_login.state_changed, portal_login.desk_changed, webhook.test and webhook.deactivated.
The full list is the events enum in the spec.
| Method | Path | What it does |
|---|---|---|
| POST | /v1/webhooks | Register a URL and its events. Returns the secret once. |
| GET | /v1/webhooks | List endpoints. Secrets are never listed. |
| GET | /v1/webhooks/{id} | Read one endpoint. |
| PATCH | /v1/webhooks/{id} | Update one endpoint. |
| DELETE | /v1/webhooks/{id} | Delete one endpoint. |
| POST | /v1/webhooks/{id}/rotate-secret | Issue a new secret. The old one works for 24 more hours. |
| POST | /v1/webhooks/test | Queue a webhook.test delivery. |
| POST | /v1/webhooks/{id}/test | Queue a webhook.test delivery to this endpoint. |
| GET | /v1/webhooks/deliveries | List deliveries. Filter by webhook_id and status. |
| GET | /v1/webhooks/{id}/deliveries | List deliveries for one endpoint. |
| POST | /v1/webhooks/{id}/deliveries/{deliveryId}/redeliver | Send one delivery again. |
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(secret, header, body, nowSec = Math.floor(Date.now() / 1000)) {
const v1s = [];
let t;
for (const piece of header.split(",")) {
const i = piece.indexOf("=");
const k = piece.slice(0, i).trim();
const v = piece.slice(i + 1).trim();
if (k === "t") t = v;
else if (k === "v1") v1s.push(v);
}
if (!t || Math.abs(nowSec - Number(t)) > 300) return false;
const expected = createHmac("sha256", secret).update(`${t}.${body}`).digest("hex");
return v1s.some((v1) => timingSafeEqual(Buffer.from(expected, "utf8"), Buffer.from(v1, "utf8")));
}
Errors
Every non-2xx response has the same shape. Branch on error.code, a stable machine string.
Show error.message to a person. Retry only when error.retryable is true, after error.retry_after seconds.
{
"error": {
"code": "insufficient_scope", // branch on this
"message": "…", // show this to a person
"retryable": false,
"retry_after": null,
"required_scope": "portal_login_mutation"
}
}
| Code | HTTP | Retry | Meaning |
|---|---|---|---|
| missing_api_key | 401 | No | No credential was sent. |
| invalid_api_key | 401 | No | The credential is invalid or expired. |
| insufficient_scope | 403 | No | The key lacks a scope. required_scope names it. |
| hitl_required | 403 | No | Saving a login, Connect or a write needs an approval. |
| attestation_required | 400 | No | Send a complete attestation object. |
| organization_required | 400 | No | A tenant key must send organization_id. |
| idempotency_mismatch | 409 | No | The same Idempotency-Key arrived with a different body. |
| attempt_in_progress | 409 | Yes | The same attempt is still running. |
| locked | 423 | No | The account is locked on the system. |
| too_many_requests | 429 | Yes | Slow down. Wait retry_after seconds. |
| connect_budget_exhausted | 429 | Yes | Too many sign-ins. next_allowed_at says when Connect may run again. |
| portal_outage | 503 | Yes | The system is not answering. The login is saved. |
| vault_error | 503 | Yes | Veraxus could not read the vault. Retry later. |
503 responses also carry a Retry-After header. The full code list, with HTTP status and retry flag for each, is in the spec.
Reference
Machine spec (OpenAPI 3.1): api.veraxus.ai/v1/openapi.json.
HTML reference: api.veraxus.ai/v1/reference.
Agents connect at api.veraxus.ai/mcp. Spec is public. Calls are not.
| Method | Path | Auth |
|---|---|---|
| GET | /v1/reference | Public |
| GET | /v1/me | Key |
| GET | /v1/organizations | Key |
| POST | /v1/organizations | Tenant key |
| POST | /v1/organizations/import | Tenant key |
| GET | /v1/credentials | Key |
| PUT | /v1/credentials | Key · approval |
| POST | /v1/portal/connect | Key · approval |
| GET | /v1/portal/mfa | Key |
| POST | /v1/portal/mfa | Key · code |
| GET | /v1/portal-logins | Key |
| GET | /v1/portal-logins/snapshot | Key |
| GET | /v1/pms/vendors | Key |
| GET | /v1/pms/connections | Key |
| GET | /v1/pms/{vendor}/operations | Key |
| POST | /v1/pms/{vendor}/execute/{operation} | Key · approval for writes |
| All | /v1/webhooks… | Tenant key · vault (see Webhooks) |
| POST | /mcp | Key or OAuth |