Skip to navigation

REST API Reference

All API endpoints require an API key passed as a Bearer token:

Authorization: Bearer mima_ext_...

Base URL: https://api.mima.works

Workspace-scoped endpoints use the path prefix /api/workspaces/{workspace_id}/governance/.


Authentication

GET /api/me

Resolve workspace and user identity from the API key. Used by the SDK to auto-resolve workspace_id.

Response

{
"user_id": "usr_...",
"workspace_id": "ws_...",
"org_id": "org_..."
}

GRC Evidence

POST /api/workspaces/{workspace_id}/governance/grc/evidence

Push a single GRC evidence record.

Request body

{
"record_type": "ai_risk_assessment",
"payload": {
"risk_level": "limited",
"risk_summary": "Customer support routing",
"intended_purpose": "Route customer queries to the correct team",
"impact_domains": ["customer_service"],
"art5_self_assessment": true
},
"system_name": "customer-support-ai",
"identity": "alice@example.com",
"resource": "customer-support-ai",
"environment": "production",
"occurred_at": "2026-06-28T09:00:00Z",
"client_sig": "abc123...",
"client_sig_algo": "hmac-sha256"
}
FieldRequiredDescription
record_typeYesOne of the 11 record types
payloadYesRecord-type-specific payload (see record-types.md)
system_nameYesAI system name
identityNoActor email or identifier
resourceNoResource affected
environmentNoproduction, staging, development
occurred_atNoISO 8601 timestamp (defaults to ingest time)
client_sigNoHMAC-SHA256 signature (see Signing below)
client_sig_algoNoMust be "hmac-sha256" when client_sig is present

Dry-run mode

Append ?dry_run=true to preview which controls would be earned without writing:

POST /api/workspaces/{workspace_id}/governance/grc/evidence?dry_run=true

Response

{
"record_id": "rec_...",
"record_type": "ai_risk_assessment",
"mapped_controls": ["EUAIA_ART9", "ISO42001_6_1"],
"detail": "ok"
}

Dry-run response — record_id is the nil UUID:

{
"record_id": "00000000-0000-0000-0000-000000000000",
"record_type": "ai_risk_assessment",
"mapped_controls": ["EUAIA_ART9", "ISO42001_6_1"],
"detail": "dry_run"
}

Attestations

POST /api/workspaces/{workspace_id}/governance/attestations/external

Push a single attestation record. Used by mima.attest() / mima.wrap().

Request body

{
"system_name": "my-pipeline",
"agent_name": "my-pipeline",
"tool_name": "generate_report",
"input_hash": "e3b0c44...",
"output_hash": "d4e8f23...",
"schema_version": 2,
"executed_at": "2026-06-28T09:00:00Z",
"model_id": "claude-opus-4-6",
"authorised_by": {
"identity": "alice@example.com",
"role": "ml-engineer"
}
}

Response

{
"attestation_id": "att_...",
"external_verified": true,
"trust_tier": "witnessed",
"detail": "ok"
}

Trust tiers

TierMeaning
witnessedHMAC/Ed25519 signature verified
declaredNo signature — accepted but not cryptographically verified

Rate limits: 2000 requests/minute per workspace. Returns 429 with a Retry-After header on excess. The SDK retries once automatically.


POST /api/workspaces/{workspace_id}/governance/attestations/batch

Push multiple attestation records in a single request.

Request body

{
"records": [
{ "system_name": "...", "tool_name": "...", "input_hash": "...", "output_hash": "...", "schema_version": 2 },
{ "system_name": "...", "tool_name": "...", "input_hash": "...", "output_hash": "...", "schema_version": 2 }
]
}

Response

{
"accepted": 2,
"rejected": 0,
"results": [
{ "status": "accepted", "attestation_id": "att_..." },
{ "status": "accepted", "attestation_id": "att_..." }
]
}

Posture

GET /api/workspaces/{workspace_id}/governance/grc/posture-brief

Returns overall posture score and per-framework breakdown.

Response

{
"overall_pct": 74,
"frameworks": [
{ "framework": "eu_ai_act", "score_pct": 71 },
{ "framework": "iso_42001", "score_pct": 79 },
{ "framework": "soc2_type2", "score_pct": 82 },
{ "framework": "iso_27001", "score_pct": 65 },
{ "framework": "nist_airf", "score_pct": 58 }
],
"failing_gates": ["soc2_type2"],
"unattested_24h": 3
}

Systems

GET /api/workspaces/{workspace_id}/governance/grc/systems

List all AI systems.

Query parameters: ?name=filter

Response

{
"systems": [
{
"system_id": "sys_...",
"system_name": "customer-support-ai",
"registered": true,
"risk_tier": "limited",
"last_evidence_at": "2026-06-28T09:00:00Z"
}
]
}

Evidence records

GET /api/workspaces/{workspace_id}/governance/grc/evidence

List evidence records.

Query parameters

ParameterDescription
system_nameFilter by system
record_typeFilter by type
daysLookback window (default: 90)
limitMax records (default: 50, max: 200)

Response

{
"records": [
{
"record_id": "rec_...",
"record_type": "model_evaluation",
"system_name": "loan-scorer",
"occurred_at": "2026-06-28T09:00:00Z",
"mapped_controls": ["EUAIA_ART9_4"],
"source": "sdk",
"signed": true
}
]
}

Gates

GET /api/workspaces/{workspace_id}/governance/grc/gates/check

Returns gate pass/fail status.

Response

{
"gates": [
{
"gate_id": "g_quarterly_eval",
"name": "Quarterly model evaluation",
"passing": true,
"blocking": false,
"exit_code": 0
},
{
"gate_id": "g_risk_assessment",
"name": "AI risk assessment",
"passing": false,
"blocking": true,
"exit_code": 2,
"reason": "No ai_risk_assessment record in the last 180 days for loan-scorer"
}
]
}

Exit codes: 0 = pass, 1 = soft fail, 2 = hard fail (blocking).


Controls

POST /api/workspaces/{workspace_id}/governance/grc/derive-controls

Get recommended record types for a system description.

Request body

{
"description": "A classification model that routes customer support tickets",
"risk_tier": "limited"
}

Response

{
"recommendations": [
{
"record_type": "ai_risk_assessment",
"priority": "required",
"controls": ["EUAIA_ART9", "ISO42001_6_1"]
},
{
"record_type": "model_evaluation",
"priority": "required",
"controls": ["EUAIA_ART9_4", "ISO42001_9_1"]
}
]
}

HMAC Signing

GRC records can include a client signature to allow auditors to verify records were created by a specific SDK instance. You keep the key — Mima never sees it.

Canonical message format

The canonical message is a compact JSON string with sorted keys and no spaces:

{"occurred_at":"2026-06-28T09:00:00Z","payload":{...sorted keys...},"record_type":"ai_risk_assessment","system_name":"loan-scorer","workspace_id":"ws_..."}

The key order is always alphabetical: occurred_at < payload < record_type < system_name < workspace_id.

The payload object’s keys are also sorted recursively (matching Python’s json.dumps(sort_keys=True, separators=(',', ':'))).

Signing

import hashlib, hmac, json
canonical = json.dumps({
"occurred_at": occurred_at,
"payload": payload,
"record_type": record_type,
"system_name": system_name,
"workspace_id": workspace_id,
}, sort_keys=True, separators=(",", ":"))
sig = hmac.new(
bytes.fromhex(signing_key),
canonical.encode(),
hashlib.sha256,
).hexdigest()

TypeScript

import { createHmac } from 'node:crypto';
// sortedStringify produces the same output as Python's json.dumps(sort_keys=True, separators=(',', ':'))
const canonical = sortedStringify({
occurred_at: occurredAt,
payload: payload,
record_type: recordType,
system_name: systemName,
workspace_id: workspaceId,
});
const sig = createHmac('sha256', Buffer.from(signingKey, 'hex'))
.update(canonical)
.digest('hex');

Both SDKs use the same canonical format and produce identical signatures.

HMAC vs Ed25519

Mima uses two signing mechanisms for different record types:

LayerAlgorithmRecord typeWhat it proves
client_sigHMAC-SHA256 (symmetric)GRC evidence recordsYour SDK instance created this record — not Mima, not a third party
witness_sigEd25519 (asymmetric)Attestation recordsVerifiable by anyone with the public key, without needing your secret

Both use the same signing_key you configure in the SDK. For GRC records it’s used as an HMAC secret; for attestations it’s used as an Ed25519 seed (the SDK derives the keypair and attaches the public verifying key to each record).

Trust tiers

trust_tierMeaning
verifiedRecord has a valid cryptographic signature
attestedSubmitted via authenticated SDK/MCP but without signing key
observedAuto-generated by the platform

Error responses

All error responses follow this shape:

{
"error": "validation_error",
"message": "record_type is required",
"field": "record_type"
}
HTTP statusError codeMeaning
400bad_requestMissing required field or invalid value
401unauthorizedInvalid or expired API key
404not_foundWorkspace or system not found
422validation_errorField-level validation failure
429rate_limitedExceeded 2000 rpm; see Retry-After header
500server_errorUnexpected server error