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
Returns 200 with { status: "healthy", timestamp, version }.
Readiness check
Returns 200 when the service and its dependencies are ready, or 503 otherwise.
OpenAPI spec
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:
| Field | Type | Description |
|---|
rules_na | number | Count of rules skipped due to when clause not matching. Absent or 0 if all rules applied. |
na_rules | NaRule[] | Details of each skipped rule — code, when clause, and human-readable reason. |
signature | string | null | Ed25519 signature over the integrity_hash. Null if signing is not configured. |
signature_kid | string | null | Key ID that produced the signature (e.g. msk_v1). |
signature_algorithm | string | null | Always 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": { ... }
}
| Field | Type | Required | Description |
|---|
actor.id | string | Yes (if actor present) | Stable identifier. Included in the integrity hash. |
actor.type | string | No | human or automated. |
actor.role | string | No | Functional role (e.g. analyst, screening_service). |
actor.authority | string | No | Authority or organisation the actor acts under. |
actor.display_name | string | No | Human-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):
| Field | Type | Required | Description |
|---|
idempotency_key | string | Yes | Client-supplied unique key for safe retries. |
chain.chain_id | string (UUID) | No | Chain ID to group this decision into an ordered sequence. |
chain.chain_step | integer (>= 1) | No | Step number within the chain. Auto-assigned if omitted. |
chain.parent_decision_id | string (UUID) | No | Parent decision within the same chain (causal lineage). |
Response fields:
| Field | Type | Description |
|---|
decision.id | string | UUID of the persisted decision. |
is_new | boolean | true on first submission, false on duplicate idempotency key. |
decision.recorded_at | string | ISO 8601 timestamp. |
decision.chain_id | string | null | Chain ID if part of a chain. |
decision.chain_step | integer | null | Step number within the chain. |
decision.parent_decision_id | string | null | Parent decision ID within the chain. |
Chain errors:
| Code | Status | Description |
|---|
CONFLICT | 409 | Duplicate chain_step within the chain. |
VALIDATION_ERROR | 400 | Parent decision not found or not in the specified chain. |
List decisions
Scope: decisions:read
Query parameters:
| Param | Type | Description |
|---|
decision_type | string | Filter by type. |
decision | string | Filter by decision (ALLOW, REVIEW, DENY, ALERT). |
from_date | string | ISO 8601 start date. |
to_date | string | ISO 8601 end date. |
policy_id | string | Filter by policy ID. |
policy_code | string | Filter by policy code. |
policy_group_id | string | Filter by policy group ID. |
policy_group_code | string | Filter by policy group code. |
source_artifact_hash | string | Filter by source artifact SHA-256 hash. |
correlation_id | string | Filter by correlation ID. |
chain_id | string (UUID) | Filter by decision chain. When set, results are sorted by chain_step ASC instead of recorded_at DESC. |
limit | number | Page size (default 50). |
offset | number | Pagination offset. |
Get decision by 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:
| Check | Method | Confidence |
|---|
| Individual receipt integrity | SHA-256 hash recomputation | Cryptographic proof |
| Chain ordering | Per-step chain hash linkage | Cryptographic proof |
| Interior deletion | Chain hash chain breaks if step removed | Cryptographic proof |
| Chain completeness | Seal hash over all receipt hashes | Cryptographic proof (if sealed) |
| Step continuity | Gap detection in chain_step sequence | Database trust |
| Parent linkage | Parent exists in chain with lower step | Database trust |
Returns 404 if no decisions exist for the given chain ID.
Per-step chain_linkage values:
| Value | Meaning |
|---|
verified | Chain integrity hash recomputed and matches, signature valid |
failed | Chain hash mismatch or signature invalid |
not_available | Step predates chain proof or signing was disabled |
Overall chain_proof values:
| Value | Meaning |
|---|
full | All steps have chain proof and verify |
partial | Mix of legacy and proven steps; all proven steps verify |
none | No steps have chain proof |
failed | At 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)
Scope: decisions:read
Opens a Server-Sent Events stream for real-time decision notifications.
Policies
List policies
Scope: policies:read
Supports pagination (limit, offset) and filtering.
Create policy
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:
| Form | Example | Matches 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
Scope: policies:read
Scope: policies:write
Updates name, description, advisory mode, group assignment, or status. Does not change rules — create a new version for that.
Deactivate policy
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:
| Field | Type | Description |
|---|
status | string | Version lifecycle state: draft, ratified, or superseded. |
ratified_by | string | null | User who ratified the version. |
ratified_at | string | null | ISO 8601 timestamp of ratification. |
Error codes:
| Code | Status | Description |
|---|
MAKER_CHECKER_VIOLATION | 403 | The ratifier is the same user who created the version (when maker-checker is enforced). |
RATIFICATION_ERROR | 400 | Version 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:
| Field | Type | Description |
|---|
version | number | Restrict to a specific policy version. |
from, to | string | ISO 8601 date range. |
limit, cursor | — | Standard pagination. |
Policy Groups
List groups
Scope: policies:read
Create group
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
Scope: alerts:read
Query parameters:
| Param | Type | Description |
|---|
severity | string | low, medium, high, critical |
acknowledged | boolean | Filter by acknowledgement status. |
Get alert
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
Scope: api-keys:admin
Create key
Scope: api-keys:admin
See Authentication for a full example.
Get key
Scope: api-keys:admin
Revoke key
Scope: api-keys:admin
Audit
List audit events
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 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.
Scope: forms:write
Scope: forms:read
Scope: forms:read
Scope: forms:write
Scope: forms:write
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.
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:
| Field | Type | Description |
|---|
decision | object | The full recorded decision receipt. |
verification_url | string | Public verifier URL for this 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
Scope: settings:read
Returns tenant-level governance configuration.
Response fields:
| Field | Type | Description |
|---|
enforce_maker_checker | boolean | If true, policy version ratification requires a different user than the version creator. |
Update settings
Scope: settings:admin
Body accepts any subset of the settings fields above.
Fields
List 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:
| Field | Type | Description |
|---|
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:
| Field | Type | Description |
|---|
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.