Developer docs

JSON in. JSON out.

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

Your company. Your customer. Their login.

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.

LayerWhoIn 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

REST for software. MCP for agents.

REST

JSON over HTTPS. Every path on this page sits under this base.

https://api.veraxus.ai/v1

MCP

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

A key, or a sign-in.

Connect a customer’s system

They sign in once. The password stays in the vault.

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.

StepRESTMCP
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

Save the login

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.

Sign in

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.

Approval

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.

Verification codes

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.

Your screen

  • One card per system per customer: username, password, Sign in. Offer Sign in only when the status is not_connected or expired.
  • Hide the code field until Connect returns needs_code.
  • After ready, show Signed in. Do not call Connect again.
  • On an interrupted run, read GET /v1/portal/mfa first. A pending hold gets the code; do not Connect again. Then read GET /v1/portal-logins.
  • Sign in with a person at the desk to receive the code. Do not request a code unattended.
  • Never store the one-time code.
  • For a fast settings screen, GET /v1/portal-logins/snapshot never contacts the system. Its status turns status_unknown once the last reading is ten minutes old.
StatusWhat you do
not_connectedNothing is signed in. Save the login, then Connect once.
needs_codeShow a code field and send the code with POST /v1/portal/mfa. Do not sign in again.
confirmation_pendingThe 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.
readySigned in. Calls reuse the saved login. Do not Connect.
reconnectingOn GET /v1/portal-logins rows only. Veraxus is signing back in by itself. Poll; do not Connect.
expiredThe system ended the session. With a person at the desk, Connect once.
credentials_invalidThe system refused the saved login. Ask your customer to update it with PUT /v1/credentials, then Connect.
lockedThe account is locked on the system. Do not retry Connect.
portal_outageThe login is saved. The system is not answering. Retry later. Do not ask for the password.
quarantinedThe login is saved. The last sign-in was blocked. Retry later. Do not ask for the password.
vault_errorVeraxus could not read the vault. Retry later. Do not ask for the password.
status_unknownNot 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

Each system publishes its operations.

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.

StepRESTWhat 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.

Read

  • Send { organization_id, input }. Validate input against the operation’s input_schema when it has one.
  • Reads reuse the saved login and never sign in. 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.

Write

  • Write access is granted per organization.
  • Every write needs an approval, the same as Connect: an approved grant in X-Veraxus-Hitl-Grant, or portal_login_mutation plus an attestation.
  • Send an Idempotency-Key of 16 to 128 characters. One key runs one write. A replay returns the stored receipt.
  • A write goes out once and is never retried. 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.

Export

An export is a read. It runs under the same saved login, organization isolation and audit as any other read operation.

When the system is down

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 fieldWhat it records
actor_user_idRequired. Your app’s id for the person who entered or approved the action, up to 200 characters. Not a Veraxus console user.
consent_session_idRequired. Your consent session or screen id that tied the action to that person, up to 200 characters.
consented_atRequired. ISO-8601. Within the last 24 hours and no more than 5 minutes in the future.
consent_kindOptional. 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

Signed events instead of polling.

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.

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.

MethodPathWhat it does
POST/v1/webhooksRegister a URL and its events. Returns the secret once.
GET/v1/webhooksList 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-secretIssue a new secret. The old one works for 24 more hours.
POST/v1/webhooks/testQueue a webhook.test delivery.
POST/v1/webhooks/{id}/testQueue a webhook.test delivery to this endpoint.
GET/v1/webhooks/deliveriesList deliveries. Filter by webhook_id and status.
GET/v1/webhooks/{id}/deliveriesList deliveries for one endpoint.
POST/v1/webhooks/{id}/deliveries/{deliveryId}/redeliverSend 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

Branch on the code. Show the message.

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"
  }
}
CodeHTTPRetryMeaning
missing_api_key401NoNo credential was sent.
invalid_api_key401NoThe credential is invalid or expired.
insufficient_scope403NoThe key lacks a scope. required_scope names it.
hitl_required403NoSaving a login, Connect or a write needs an approval.
attestation_required400NoSend a complete attestation object.
organization_required400NoA tenant key must send organization_id.
idempotency_mismatch409NoThe same Idempotency-Key arrived with a different body.
attempt_in_progress409YesThe same attempt is still running.
locked423NoThe account is locked on the system.
too_many_requests429YesSlow down. Wait retry_after seconds.
connect_budget_exhausted429YesToo many sign-ins. next_allowed_at says when Connect may run again.
portal_outage503YesThe system is not answering. The login is saved.
vault_error503YesVeraxus 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

The spec is the contract.

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.

MethodPathAuth
GET/v1/referencePublic
GET/v1/meKey
GET/v1/organizationsKey
POST/v1/organizationsTenant key
POST/v1/organizations/importTenant key
GET/v1/credentialsKey
PUT/v1/credentialsKey · approval
POST/v1/portal/connectKey · approval
GET/v1/portal/mfaKey
POST/v1/portal/mfaKey · code
GET/v1/portal-loginsKey
GET/v1/portal-logins/snapshotKey
GET/v1/pms/vendorsKey
GET/v1/pms/connectionsKey
GET/v1/pms/{vendor}/operationsKey
POST/v1/pms/{vendor}/execute/{operation}Key · approval for writes
All/v1/webhooks…Tenant key · vault (see Webhooks)
POST/mcpKey or OAuth