Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.meshqu.com/llms.txt

Use this file to discover all available pages before exploring further.

Base path: /v1 Determinism guarantee: Given the same decision context and the same policy snapshot, MeshQu will always return the same verdict. The evaluated_rules_hash and integrity_hash in each response let you verify this independently. Determinism applies strictly to MeshQu’s policy evaluation logic and snapshot resolution. Client-supplied data and external evidence are outside this guarantee.
Example: Replaying a decision six months later will produce the same verdict as long as the same policy snapshot is used, even if current policies have changed.
Evaluations are designed for synchronous use at decision boundaries. Typical response times are single-digit milliseconds for policy evaluation, excluding network latency. Clients should still implement timeouts and fallback strategies. All authenticated endpoints require Authorization: Bearer <API_KEY> and X-MeshQu-Tenant-Id headers. See Authentication.

Operations (public, no auth)

Health check

GET /v1/health
Returns 200 with { status: "healthy", timestamp, version }.

Readiness check

GET /v1/ready
Returns 200 when the service and its dependencies are ready, or 503 otherwise.

OpenAPI spec

GET /openapi.json
Returns the OpenAPI 3.0 specification.

Decisions

Evaluate (dry-run)

POST /v1/decisions/evaluate
Scope: decisions:evaluate Evaluates a decision context against active policies. Nothing is persisted. What you send:
{
  "context": {
    "decision_type": "trade_execution",
    "fields": {
      "account_id": "ACC-001",
      "instrument": "AAPL",
      "quantity": 100
    },
    "metadata": { "source": "order-service" },
    "evidence": []
  }
}
What you get back:
{
  "result": {
    "decision": "ALLOW",
    "violations": [],
    "rules_evaluated": 3,
    "rules_na": 1,
    "na_rules": [
      {
        "rule_code": "JURISDICTION-001",
        "when": { "field": "currency", "in": ["GBP", "CHF"] },
        "reason": "currency is \"USD\", not one of [\"GBP\", \"CHF\"]"
      }
    ],
    "evaluation_time_ms": 8.4,
    "timestamp": "2025-01-15T10:30:00.000Z",
    "policy_snapshot_id": "11111111-1111-1111-1111-111111111111",
    "evaluated_rules_hash": "2c26b46b68ffc68ff99b453c1d30413413422...sha256",
    "integrity_hash": "6f1ed002ab5595859014ebf0951522...sha256",
    "signature": "base64url-encoded-ed25519-signature",
    "signature_kid": "msk_v1",
    "signature_algorithm": "ed25519"
  },
  "snapshot": {
    "id": "11111111-1111-1111-1111-111111111111",
    "hash": "2c26b46b68ffc68ff99b453c1d30413413422...sha256",
    "rules_count": 3,
    "policies_count": 1,
    "cached": true
  },
  "duration_ms": 12.7,
  "advisory_mode": {
    "enabled": false
  },
  "governance_source": {
    "tenant_id": "tenant-uuid",
    "tenant_name": "Acme Trading",
    "level": 0
  }
}
Receipt fields:
FieldTypeDescription
rules_nanumberCount of rules skipped due to when clause not matching. Absent or 0 if all rules applied.
na_rulesNaRule[]Details of each skipped rule — code, when clause, and human-readable reason.
signaturestring | nullEd25519 signature over the integrity_hash. Null if signing is not configured.
signature_kidstring | nullKey ID that produced the signature (e.g. msk_v1).
signature_algorithmstring | nullAlways ed25519 when present.

**Example response (DENY):**

```json
{
  "result": {
    "decision": "DENY",
    "violations": [
      {
        "rule_code": "THRESHOLD-001",
        "severity": "critical",
        "reason_code": "VALUE_ABOVE_MAX",
        "reason": "Field 'quantity' value 15000 exceeds maximum 10000",
        "field": "quantity",
        "actual_value": 15000,
        "expected_value": "<= 10000"
      }
    ],
    "rules_evaluated": 3,
    "evaluation_time_ms": 1.2,
    "timestamp": "2025-01-15T10:30:00.000Z",
    "policy_snapshot_id": "11111111-1111-1111-1111-111111111111",
    "evaluated_rules_hash": "2c26b46b68ffc68ff99b453c1d...sha256",
    "integrity_hash": "6f1ed002ab5595859014ebf09...sha256"
  },
  "snapshot": {
    "id": "11111111-1111-1111-1111-111111111111",
    "hash": "2c26b46b68ffc68ff99b453c1d...sha256",
    "rules_count": 3,
    "policies_count": 1,
    "cached": true
  },
  "duration_ms": 8.3
}

Record a decision

POST /v1/decisions/record
Scope: decisions:write Evaluates and persists the result. Requires an idempotency_key. Request body shape:
{
  "context": { ... },
  "options": {
    "idempotency_key": "...",
    "chain": {
      "chain_id": "uuid",
      "chain_step": 1,
      "parent_decision_id": "uuid"
    }
  }
}
Actor attribution (optional): Include an actor object at the top level of the request body to record who or what triggered the decision:
{
  "context": { ... },
  "actor": {
    "id": "user-uuid-or-service-name",
    "type": "human",
    "role": "compliance_officer",
    "authority": "MLRO",
    "display_name": "Jane Smith"
  },
  "options": { ... }
}
FieldTypeRequiredDescription
actor.idstringYes (if actor present)Stable identifier. Included in the integrity hash.
actor.typestringNohuman or automated.
actor.rolestringNoFunctional role (e.g. analyst, screening_service).
actor.authoritystringNoAuthority or organisation the actor acts under.
actor.display_namestringNoHuman-readable name for audit display.
Backward compatibility: If actor is omitted but context.metadata.actor_id is present, the API falls back to using that value. When actor.id is supplied, it takes precedence over context.metadata.actor_id.
Request fields (under options):
FieldTypeRequiredDescription
idempotency_keystringYesClient-supplied unique key for safe retries.
chain.chain_idstring (UUID)NoChain ID to group this decision into an ordered sequence.
chain.chain_stepinteger (>= 1)NoStep number within the chain. Auto-assigned if omitted.
chain.parent_decision_idstring (UUID)NoParent decision within the same chain (causal lineage).
Response fields:
FieldTypeDescription
decision.idstringUUID of the persisted decision.
is_newbooleantrue on first submission, false on duplicate idempotency key.
decision.recorded_atstringISO 8601 timestamp.
decision.chain_idstring | nullChain ID if part of a chain.
decision.chain_stepinteger | nullStep number within the chain.
decision.parent_decision_idstring | nullParent decision ID within the chain.
Chain errors:
CodeStatusDescription
CONFLICT409Duplicate chain_step within the chain.
VALIDATION_ERROR400Parent decision not found or not in the specified chain.

List decisions

GET /v1/decisions
Scope: decisions:read Query parameters:
ParamTypeDescription
decision_typestringFilter by type.
decisionstringFilter by decision (ALLOW, REVIEW, DENY, ALERT).
from_datestringISO 8601 start date.
to_datestringISO 8601 end date.
policy_idstringFilter by policy ID.
policy_codestringFilter by policy code.
policy_group_idstringFilter by policy group ID.
policy_group_codestringFilter by policy group code.
source_artifact_hashstringFilter by source artifact SHA-256 hash.
correlation_idstringFilter by correlation ID.
chain_idstring (UUID)Filter by decision chain. When set, results are sorted by chain_step ASC instead of recorded_at DESC.
limitnumberPage size (default 50).
offsetnumberPagination offset.

Get decision by ID

GET /v1/decisions/:id
Scope: decisions:read

Replay decision

POST /v1/decisions/:id/replay
Scope: decisions:read Re-evaluates a recorded decision for audit verification. Returns the replayed result alongside the original for comparison.

Record decision outcome

POST /v1/decisions/:id/outcome
Scope: decisions:write Records what happened after a verdict. One outcome per decision (409 on duplicate). What you send:
{
  "status": "overridden",
  "final_action": "ALLOW",
  "source_type": "human",
  "reported_by": "compliance-officer@acme.com",
  "resolution_reason": "Manual evidence confirmed the document is valid"
}
What you get back (201):
{
  "id": "22222222-2222-4222-8222-222222222222",
  "decision_id": "11111111-1111-4111-8111-111111111111",
  "tenant_id": "00000000-0000-4000-8000-000000000001",
  "status": "overridden",
  "final_action": "ALLOW",
  "source_type": "human",
  "reported_by": "compliance-officer@acme.com",
  "reported_at": "2025-01-15T14:00:00.000Z",
  "resolution_reason": "Manual evidence confirmed the document is valid",
  "notes": null,
  "external_reference": null,
  "created_at": "2025-01-15T14:00:00.000Z"
}

Verify decision chain

POST /v1/decisions/chain/:chainId/verify
Scope: decisions:read Verifies all decisions in a chain. For each step, recomputes the integrity hash and checks structural consistency (step continuity, parent linkage). What you get back (200):
{
  "valid": true,
  "chain_id": "7f3a2b1c-...",
  "steps_checked": 3,
  "receipts_verified": 3,
  "receipts_failed": 0,
  "chain_proof": "full",
  "chain_sealed": true,
  "seal": {
    "id": "seal-uuid",
    "steps_count": 3,
    "seal_hash": "sha256-hex...",
    "seal_verified": true,
    "sealed_at": "2026-04-04T15:00:00.000Z"
  },
  "warnings": [],
  "sequence": [
    { "step": 1, "decision_id": "aaa-...", "decision": "REVIEW", "integrity_verified": true, "chain_linkage": "verified" },
    { "step": 2, "decision_id": "bbb-...", "decision": "ALLOW", "integrity_verified": true, "chain_linkage": "verified" },
    { "step": 3, "decision_id": "ccc-...", "decision": "ALLOW", "integrity_verified": true, "chain_linkage": "verified" }
  ],
  "verified_at": "2026-04-04T12:00:00.000Z"
}
Verification scope:
CheckMethodConfidence
Individual receipt integritySHA-256 hash recomputationCryptographic proof
Chain orderingPer-step chain hash linkageCryptographic proof
Interior deletionChain hash chain breaks if step removedCryptographic proof
Chain completenessSeal hash over all receipt hashesCryptographic proof (if sealed)
Step continuityGap detection in chain_step sequenceDatabase trust
Parent linkageParent exists in chain with lower stepDatabase trust
Returns 404 if no decisions exist for the given chain ID. Per-step chain_linkage values:
ValueMeaning
verifiedChain integrity hash recomputed and matches, signature valid
failedChain hash mismatch or signature invalid
not_availableStep predates chain proof or signing was disabled
Overall chain_proof values:
ValueMeaning
fullAll steps have chain proof and verify
partialMix of legacy and proven steps; all proven steps verify
noneNo steps have chain proof
failedAt least one chain proof failed

Seal a chain

POST /v1/decisions/chain/:chainId/seal
Scope: decisions:write Seals a chain, proving completeness. No more steps can be added after sealing. Requires signing to be configured. What you get back (201):
{
  "id": "seal-uuid",
  "chain_id": "7f3a2b1c-...",
  "steps_count": 3,
  "seal_hash": "sha256-hex-64-chars",
  "seal_signature": "base64url-ed25519-sig",
  "seal_signature_kid": "msk_v1",
  "seal_signature_algorithm": "ed25519",
  "transparency_anchor": null,
  "sealed_at": "2026-04-04T15:00:00.000Z",
  "created_at": "2026-04-04T15:00:00.000Z"
}
Returns 409 if already sealed, 400 if chain is empty or signing not configured, 404 if chain not found.
Sealing is irreversible. Once sealed, no more steps can be added to the chain. If a chain is sealed prematurely, start a new chain.

Get chain seal

GET /v1/decisions/chain/:chainId/seal
Scope: decisions:read Returns the seal for a chain, or 404 if not sealed.

Decision stream (SSE)

GET /v1/decisions/stream
Scope: decisions:read Opens a Server-Sent Events stream for real-time decision notifications.

Policies

List policies

GET /v1/policies
Scope: policies:read Supports pagination (limit, offset) and filtering.

Create policy

POST /v1/policies
Scope: policies:write What you send:
{
  "code": "max-order-size",
  "name": "Max Order Size",
  "description": "Rejects orders exceeding configured thresholds.",
  "decision_type": "trade_execution",
  "advisory_mode": false,
  "rules": [
    {
      "rule_code": "SIZE-001",
      "type": "threshold",
      "field": "quantity",
      "operator": "<=",
      "value": 10000,
      "severity": "high",
      "when": { "field": "instrument_type", "equals": "equity" }
    }
  ]
}
Rules support an optional when clause for conditional applicability. A rule with when is only evaluated when the clause matches the decision context — otherwise it returns NA (not applicable) and does not affect the outcome. Supported when operators:
FormExampleMatches when
equals{ "field": "currency", "equals": "USD" }Field value is exactly "USD".
in{ "field": "currency", "in": ["USD", "EUR"] }Field value is one of the listed values.
exists{ "field": "credit_score", "exists": true }Field is present (or absent if false).
all{ "all": [clause, clause] }All nested clauses match.
any{ "any": [clause, clause] }At least one nested clause matches.

Get policy

GET /v1/policies/:id
Scope: policies:read

Update policy metadata

PATCH /v1/policies/:id
Scope: policies:write Updates name, description, advisory mode, group assignment, or status. Does not change rules — create a new version for that.

Deactivate policy

DELETE /v1/policies/:id
Scope: policies:write Soft-deletes (deactivates) the policy. It will no longer be evaluated.

Validate policy (dry-run)

POST /v1/policies/validate
Scope: policies:read Validates policy configuration without creating or modifying any policy.

Create policy version

POST /v1/policies/:id/versions
Scope: policies:write Creates a new immutable version of the policy’s rules. The version is created in draft status and must be ratified before it governs evaluations. What you send:
{
  "rules": [],
  "change_reason": "Increased threshold from 500 to 1000"
}

List policy versions

GET /v1/policies/:id/versions
Scope: policies:read

Get policy version

GET /v1/policies/:id/versions/:version
Scope: policies:read

Ratify policy version

POST /v1/policies/:id/versions/:version/ratify
Scope: policies:write Promotes a draft version to ratified status, making it the active version governing evaluations. The previously active version is automatically superseded. New versions are created in draft status and must be ratified before they take effect. When maker-checker enforcement is enabled (via tenant governance settings), the ratifier must be a different user than the version creator. Response fields:
FieldTypeDescription
statusstringVersion lifecycle state: draft, ratified, or superseded.
ratified_bystring | nullUser who ratified the version.
ratified_atstring | nullISO 8601 timestamp of ratification.
Error codes:
CodeStatusDescription
MAKER_CHECKER_VIOLATION403The ratifier is the same user who created the version (when maker-checker is enforced).
RATIFICATION_ERROR400Version is not in draft status or other validation failure.

Submit policy version

POST /v1/policies/:id/versions/:version/submit
Scope: policies:write Submits a draft version for ratification. The version moves from draft to pending_ratification.

Reject policy version

POST /v1/policies/:id/versions/:version/reject
Scope: policies:write Rejects a submitted version. Optional body field reason is recorded on the audit trail.

Recall policy version

POST /v1/policies/:id/versions/:version/recall
Scope: policies:write Recalls a submitted version back to draft so the author can revise it.

Update draft rules

PATCH /v1/policies/:id/versions/:version
Scope: policies:write Replaces the rules on a draft version. Rejected with 400 if the version is already ratified or superseded.

Delete draft version

DELETE /v1/policies/:id/versions/:version
Scope: policies:write Deletes a draft version. Ratified versions cannot be deleted — supersede them by creating a new version instead.

List decisions for a policy

GET /v1/policies/:id/decisions
Scope: policies:read, decisions:read Returns recorded decisions that were evaluated against this policy, across all versions. Query parameters:
FieldTypeDescription
versionnumberRestrict to a specific policy version.
from, tostringISO 8601 date range.
limit, cursorStandard pagination.

Policy Groups

List groups

GET /v1/policy-groups
Scope: policies:read

Create group

POST /v1/policy-groups
Scope: policies:write

Get group

GET /v1/policy-groups/:id
Scope: policies:read

Update group

PATCH /v1/policy-groups/:id
Scope: policies:write

Delete group

DELETE /v1/policy-groups/:id
Scope: policies:write

List policies in group

GET /v1/policy-groups/:id/policies
Scope: policies:read

Alerts

List alerts

GET /v1/alerts
Scope: alerts:read Query parameters:
ParamTypeDescription
severitystringlow, medium, high, critical
acknowledgedbooleanFilter by acknowledgement status.

Get alert

GET /v1/alerts/:id
Scope: alerts:read

Acknowledge alert

POST /v1/alerts/:id/acknowledge
Scope: alerts:write What you send:
{
  "acknowledged_by": "ops-team",
  "notes": "Reviewed and escalated to compliance."
}

Webhook Subscriptions

See Webhooks guide for full details.

Create subscription

POST /v1/alerts/subscriptions
Scope: alerts:write The response includes a secret value for HMAC verification. It is shown only once.

List subscriptions

GET /v1/alerts/subscriptions
Scope: alerts:read

Delete subscription

DELETE /v1/alerts/subscriptions/:id
Scope: alerts:write

Webhook deliveries

GET /v1/alerts/deliveries
GET /v1/alerts/deliveries/stats
GET /v1/alerts/deliveries/:id
POST /v1/alerts/deliveries/:id/retry
Scope: alerts:read (list/stats/get), alerts:write (retry)

API Keys

List keys

GET /v1/api-keys
Scope: api-keys:admin

Create key

POST /v1/api-keys
Scope: api-keys:admin See Authentication for a full example.

Get key

GET /v1/api-keys/:id
Scope: api-keys:admin

Revoke key

DELETE /v1/api-keys/:id
Scope: api-keys:admin

Audit

List audit events

GET /v1/audit-log
Scope: audit:read Paginated listing of audit events with filtering support.

Get entity history

GET /v1/audit-log/entity/:type/:id
Scope: audit:read Returns all audit events for a specific entity in chronological order.

Verify audit log integrity

POST /v1/audit-log/verify
Scope: audit:admin Verifies the integrity of an audit event.

Forms (Human Decision Capture)

Forms let you capture structured human decisions that produce receipts identical in shape to programmatic decisions. A form binds a decision type, a target policy, and an input schema. Submitting a form runs evaluation and records the result.

Create form

POST /v1/forms
Scope: forms:write

List forms

GET /v1/forms
Scope: forms:read

Get form

GET /v1/forms/:formId
Scope: forms:read

Update form

PATCH /v1/forms/:formId
Scope: forms:write

Delete form

DELETE /v1/forms/:formId
Scope: forms:write

Get input schema

GET /v1/forms/:formId/schema
Scope: forms:read Returns the JSON Schema describing the form’s input shape — suitable for rendering a form UI or validating client-side.

Get API contract

GET /v1/forms/:formId/api-contract
Scope: forms:read Returns the request/response contract an integrating system needs to submit this form programmatically.

Submit form

POST /v1/forms/:formId/submit
Scope: forms:submit Submits a form, evaluates against the bound policy, records a decision, and returns the decision and verification URL. The actor on the resulting decision is the submitter. Response fields:
FieldTypeDescription
decisionobjectThe full recorded decision receipt.
verification_urlstringPublic verifier URL for this receipt.

Get form submission receipt

GET /v1/forms/:formId/receipts/:decisionId
Scope: forms:read Returns the decision receipt produced by a previous form submission, with the form’s binding metadata attached.

Tenant Settings

Get settings

GET /v1/settings
Scope: settings:read Returns tenant-level governance configuration. Response fields:
FieldTypeDescription
enforce_maker_checkerbooleanIf true, policy version ratification requires a different user than the version creator.

Update settings

PATCH /v1/settings
Scope: settings:admin Body accepts any subset of the settings fields above.

Fields

List fields

GET /v1/fields
Scope: fields:read Returns the field catalog discovered from decision contexts seen for this tenant. Used by the policy editor to suggest field paths.

Public Verification

These endpoints are unauthenticated — they do not require an API key or tenant header. They are intended for offline verifiers, third-party auditors, and counterparties holding a receipt or chain ID.

Get receipt

GET /v1/receipts/:decisionId
Returns a signed decision receipt by ID. The receipt is enough to verify integrity and signature offline given the corresponding public signing key (see JWKS).

Get receipt bundle

GET /v1/receipts/:decisionId/bundle
Query parameters:
FieldTypeDescription
format'json' | 'tar'Bundle format. Default json.
Returns a full Verification Bundle — receipt + policy snapshot + chain proofs + transparency proof + trust roots, under a digest-of-digests manifest. See Verification Bundle for the on-disk layout and sub-claim taxonomy.

Verify chain

GET /v1/chains/:chainId/verify
Computes and returns the verification result for a decision chain. The response identifies each step receipt and whether all chain-link signatures verify.

Get chain seal

GET /v1/chains/:chainId/seal
Returns the chain seal record if one exists. Sealing itself is performed via the authenticated POST /v1/decisions/chain/:chainId/seal endpoint.

Get chain bundle

GET /v1/chains/:chainId/bundle
Query parameters:
FieldTypeDescription
format'json' | 'tar'Bundle format. Default json.
Returns a chain-level Verification Bundle covering every step receipt in the chain plus the chain seal (if sealed).

JWKS

GET /v1/.well-known/signing-keys
Returns the versioned public signing keys used by MeshQu, as a JWKS document. Verifiers should fetch these out-of-band, pin fingerprints, and use them as trust roots for receipt signature verification. The bundle’s trusted_keys.json file is informational only and must not be used as a trust root.