Skip to main content

Mandaitor Delegation Mandate Registry API (1.0.0)

Download OpenAPI specification:Download

Mandaitor API Support: api@mandaitor.io License: Proprietary

Neutral, trustworthy infrastructure for verifiable delegated authority between humans and AI agents.

Admin

Admin-only operations

List onboarding access requests (admin only)

Lists onboarding access requests. Supports optional status filtering (PENDING, APPROVED, REJECTED) and cursor-based pagination. Requires Cognito JWT with mandaitor-admins group membership.

Authorizations:
BearerAuth
query Parameters
status
string
Enum: "PENDING" "APPROVED" "REJECTED"

Filter by request status

limit
integer [ 1 .. 200 ]
Default: 50
cursor
string

Base64url-encoded pagination cursor

Responses

Response samples

Content type
application/json
{
  • "requests": [
    ],
  • "next_cursor": "string"
}

Approve or reject an access request

Processes an admin action on an onboarding request. Supported values for {action}: approve, reject.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
requestId
required
string
action
required
string
Enum: "approve" "reject"
Request Body schema: application/json
notes
string

Responses

Request samples

Content type
application/json
{
  • "notes": "string"
}

Response samples

Content type
application/json
{
  • "tenant_id": "string",
  • "company_name": "string",
  • "plan": "string",
  • "status": "ACTIVE",
  • "created_at": "2019-08-24T14:15:22Z",
  • "api_keys": [
    ]
}

Get tenant detail (admin)

Returns a single tenant's config, linked onboarding metadata, API key count, and mandate status counts (capped at the first 600 mandates).

Requires Cognito JWT with mandaitor-admins group membership.

Authorizations:
BearerAuth
path Parameters
id
required
string
Example: tnt_ABC123

Responses

Response samples

Content type
application/json
{
  • "tenant_id": "string",
  • "display_name": "string",
  • "contact_name": "string",
  • "contact_email": "string",
  • "status": "ACTIVE",
  • "created_at": "2019-08-24T14:15:22Z",
  • "usage_plan": {
    },
  • "api_key_count": 0,
  • "mandate_stats": {
    },
  • "onboarding": {
    }
}

Emergency kill-switch — stop a tenant's executions and revoke its mandates

EMERGENCY OPERATION — Stops what a tenant's agents may do and revokes its ACTIVE, SUSPENDED and PENDING_APPROVAL mandates. This is an irreversible bulk operation intended for security incidents, compliance breaches, or contract terminations.

It takes effect on the execution path before it answers: every tool the tenant has enabled is disabled, so from then on no execution is created, approved or dispatched, and the MCP server lists no tool. The tenant enables its tools again, one by one, once the incident is over; the execution lists them as tools_disabled. While it runs, a request-bound decision is DENY with KILL_SWITCH, and an authorization given before it is not redeemed.

The rest runs asynchronously. The call records an execution, queues it and returns 202 Accepted with an execution_id. A worker then stops the tenant's executions waiting for an approval or for their dispatch, each DENIED with KILL_SWITCH (one already dispatched finishes: nothing recalls it), and revokes the mandates page by page, checkpointing its progress on the execution, which GET /admin/tenants/{id}/kill-switch/{executionId} reports until the status is COMPLETED or FAILED. Executions for one tenant run one at a time; calling again while one is running queues a second execution that finds nothing left to stop.

Requires Cognito JWT with mandaitor-admins group membership. A MANDATE_REVOKED audit event is emitted per mandate with details.kill_switch_triggered = true and details.execution_id, and an EXECUTION_DENIED event per execution stopped, with details.stage = kill_switch and details.kill_switch_execution_id.

Authorizations:
BearerAuth
path Parameters
id
required
string
Example: tnt_ABC123
Request Body schema: application/json
required
reason
required
string <= 1000 characters

Mandatory reason for the emergency kill-switch

Responses

Request samples

Content type
application/json
{
  • "reason": "Security incident — unauthorized access detected"
}

Response samples

Content type
application/json
{
  • "tenant_id": "string",
  • "execution_id": "ks_01HXYZ...",
  • "status": "QUEUED",
  • "status_path": "/admin/tenants/tnt_ABC123/kill-switch/ks_01HXYZ...",
  • "requested_at": "2019-08-24T14:15:22Z",
  • "requested_by": "string"
}

List recent kill-switch executions for a tenant

The twenty most recent kill-switch executions for the tenant, newest first, with their status and counts. Requires Cognito JWT with mandaitor-admins group membership.

Authorizations:
BearerAuth
path Parameters
id
required
string
Example: tnt_ABC123

Responses

Response samples

Content type
application/json
{
  • "tenant_id": "string",
  • "executions": [
    ]
}

Get the status of a kill-switch execution

Progress of one kill-switch execution: status, how many mandates were revoked, skipped or failed so far, and the last error if any. Poll this after a 202 until status is COMPLETED or FAILED. Requires Cognito JWT with mandaitor-admins group membership.

Authorizations:
BearerAuth
path Parameters
id
required
string
Example: tnt_ABC123
executionId
required
string
Example: ks_01HXYZ...

Responses

Response samples

Content type
application/json
{
  • "execution_id": "string",
  • "tenant_id": "string",
  • "status": "QUEUED",
  • "reason": "string",
  • "requested_by": "string",
  • "requested_at": "2019-08-24T14:15:22Z",
  • "started_at": "2019-08-24T14:15:22Z",
  • "finished_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "phase": "TOOLS",
  • "tools_disabled": [
    ],
  • "executions_cancelled": 0,
  • "executions_in_flight": 0,
  • "executions_busy": 0,
  • "executions_failed": 0,
  • "revoked_count": 0,
  • "skipped_count": 0,
  • "failed_count": 0,
  • "audit_event_failures": 0,
  • "pages_processed": 0,
  • "continuations": 0,
  • "attempts": 0,
  • "last_error": "string"
}

Get current system operational mode

Returns the current system mode (growth, sustain, maintenance, frozen) and associated metadata. Requires Cognito JWT with mandaitor-admins group membership.

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
{
  • "mode": "growth",
  • "reason": "Default operational mode",
  • "changed_at": "2019-08-24T14:15:22Z",
  • "changed_by": "string",
  • "estimated_duration_minutes": 0
}

Set system operational mode

Changes the system operational mode. This immediately affects all API endpoints:

  • growth — Normal operation. All features active.
  • sustain — Normal operation. No new features (policy only).
  • maintenance — All authenticated endpoints return 503.
  • frozen — Write endpoints return 503. Reads still work.

Mode changes propagate within ~10 seconds via in-memory cache TTL. Requires Cognito JWT with mandaitor-admins group membership.

Authorizations:
BearerAuth
Request Body schema: application/json
required
mode
required
string
Enum: "growth" "sustain" "maintenance" "frozen"
reason
string
estimated_duration_minutes
integer

Responses

Request samples

Content type
application/json
{
  • "mode": "maintenance",
  • "reason": "CDK stack update — estimated 30 minutes",
  • "estimated_duration_minutes": 30
}

Response samples

Content type
application/json
{
  • "mode": "growth",
  • "reason": "Default operational mode",
  • "changed_at": "2019-08-24T14:15:22Z",
  • "changed_by": "string",
  • "estimated_duration_minutes": 0
}

Authentication proxy for Cognito operations

Proxies authentication requests to Cognito from the browser, bypassing the ESC Cognito IDP endpoint DNS resolution issue. The {action} path parameter determines the operation:

  • login — InitiateAuth with USER_PASSWORD_AUTH
  • respond — RespondToAuthChallenge (NEW_PASSWORD_REQUIRED, MFA)
  • refresh — REFRESH_TOKEN_AUTH
  • sign-out — GlobalSignOut
  • forgot-password — ForgotPassword
  • confirm-forgot-password — ConfirmForgotPassword
path Parameters
action
required
string
Enum: "login" "respond" "refresh" "sign-out" "forgot-password" "confirm-forgot-password"
Request Body schema: application/json
required
object

Request body varies by action:

  • login: { email, password }
  • respond: { challengeName, session, responses }
  • refresh: { refreshToken }
  • sign-out: { accessToken }
  • forgot-password: { email }
  • confirm-forgot-password: { email, code, newPassword }

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
{ }

Reconcile product-owned Stripe Customers

Admin-only, dry-run-first Customer inventory. Every mapped Customer is retrieved read-only and checked against the exact Mandaitor product, stage, and opaque owner metadata. The default request never writes. { "apply": true } creates Customers only for unmapped tenants and is rejected unless BILLING_CUSTOMER_SYNC_APPLY_ENABLED=true. It never creates subscriptions and is not scheduled automatically.

Authorizations:
ApiKeyAuthBearerAuth
Request Body schema: application/json
optional
apply
boolean
Default: false

Request gated Customer creation for unmapped tenants

Responses

Request samples

Content type
application/json
{
  • "apply": false
}

Response samples

Content type
application/json
{
  • "mode": "dry_run",
  • "scanned": 0,
  • "verified_owned": 0,
  • "legacy_mapping_requires_review": 0,
  • "missing_customer": 0,
  • "deleted_customer": 0,
  • "metadata_mismatch": 0,
  • "remote_lookup_failed": 0,
  • "created": 0,
  • "failed": 0
}

Agent Marketplace

Agent discovery, catalog listings, capability matching, and reviews

Create a new marketplace listing

Authorizations:
BearerAuth
Request Body schema: application/json
required
agent_id
required
string
display_name
required
string
tagline
required
string <= 120 characters
description
required
string
logo_url
string <uri>
docs_url
string <uri>
required
Array of objects (CapabilityDeclaration) non-empty
required
object (PricingInfo)
tags
Array of strings
supported_regions
Array of strings
certifications
Array of strings

Responses

Request samples

Content type
application/json
{
  • "agent_id": "string",
  • "display_name": "string",
  • "tagline": "string",
  • "description": "string",
  • "logo_url": "http://example.com",
  • "docs_url": "http://example.com",
  • "capabilities": [
    ],
  • "pricing": {
    },
  • "tags": [
    ],
  • "supported_regions": [
    ],
  • "certifications": [
    ]
}

Response samples

Content type
application/json
{
  • "listing_id": "string",
  • "agent_id": "string",
  • "owner_tenant_id": "string",
  • "status": "DRAFT",
  • "display_name": "string",
  • "tagline": "string",
  • "description": "string",
  • "logo_url": "http://example.com",
  • "docs_url": "http://example.com",
  • "capabilities": [
    ],
  • "pricing": {
    },
  • "trust_metrics": {
    },
  • "tags": [
    ],
  • "supported_regions": [
    ],
  • "certifications": [
    ],
  • "version": 0,
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "published_at": "2019-08-24T14:15:22Z"
}

List own marketplace listings

Authorizations:
BearerAuth
query Parameters
cursor
string
limit
integer [ 1 .. 50 ]
Default: 20

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string",
  • "total_count": 0
}

Get listing details

Authorizations:
BearerAuth
path Parameters
listingId
required
string

Responses

Response samples

Content type
application/json
{
  • "listing_id": "string",
  • "agent_id": "string",
  • "owner_tenant_id": "string",
  • "status": "DRAFT",
  • "display_name": "string",
  • "tagline": "string",
  • "description": "string",
  • "logo_url": "http://example.com",
  • "docs_url": "http://example.com",
  • "capabilities": [
    ],
  • "pricing": {
    },
  • "trust_metrics": {
    },
  • "tags": [
    ],
  • "supported_regions": [
    ],
  • "certifications": [
    ],
  • "version": 0,
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "published_at": "2019-08-24T14:15:22Z"
}

Update listing

Authorizations:
BearerAuth
path Parameters
listingId
required
string
Request Body schema: application/json
required
display_name
string
tagline
string <= 120 characters
description
string
logo_url
string <uri>
docs_url
string <uri>
Array of objects (CapabilityDeclaration)
object (PricingInfo)
tags
Array of strings
supported_regions
Array of strings
certifications
Array of strings

Responses

Request samples

Content type
application/json
{
  • "display_name": "string",
  • "tagline": "string",
  • "description": "string",
  • "logo_url": "http://example.com",
  • "docs_url": "http://example.com",
  • "capabilities": [
    ],
  • "pricing": {
    },
  • "tags": [
    ],
  • "supported_regions": [
    ],
  • "certifications": [
    ]
}

Response samples

Content type
application/json
{
  • "listing_id": "string",
  • "agent_id": "string",
  • "owner_tenant_id": "string",
  • "status": "DRAFT",
  • "display_name": "string",
  • "tagline": "string",
  • "description": "string",
  • "logo_url": "http://example.com",
  • "docs_url": "http://example.com",
  • "capabilities": [
    ],
  • "pricing": {
    },
  • "trust_metrics": {
    },
  • "tags": [
    ],
  • "supported_regions": [
    ],
  • "certifications": [
    ],
  • "version": 0,
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "published_at": "2019-08-24T14:15:22Z"
}

Archive listing (soft-delete)

Authorizations:
BearerAuth
path Parameters
listingId
required
string

Responses

Response samples

Content type
application/json
{
  • "error": "BAD_REQUEST",
  • "message": "principal and delegate are required"
}

Publish a draft listing

Authorizations:
BearerAuth
path Parameters
listingId
required
string

Responses

Response samples

Content type
application/json
{
  • "listing_id": "string",
  • "agent_id": "string",
  • "owner_tenant_id": "string",
  • "status": "DRAFT",
  • "display_name": "string",
  • "tagline": "string",
  • "description": "string",
  • "logo_url": "http://example.com",
  • "docs_url": "http://example.com",
  • "capabilities": [
    ],
  • "pricing": {
    },
  • "trust_metrics": {
    },
  • "tags": [
    ],
  • "supported_regions": [
    ],
  • "certifications": [
    ],
  • "version": 0,
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "published_at": "2019-08-24T14:15:22Z"
}

Suspend a published listing

Authorizations:
BearerAuth
path Parameters
listingId
required
string

Responses

Response samples

Content type
application/json
{
  • "listing_id": "string",
  • "agent_id": "string",
  • "owner_tenant_id": "string",
  • "status": "DRAFT",
  • "display_name": "string",
  • "tagline": "string",
  • "description": "string",
  • "logo_url": "http://example.com",
  • "docs_url": "http://example.com",
  • "capabilities": [
    ],
  • "pricing": {
    },
  • "trust_metrics": {
    },
  • "tags": [
    ],
  • "supported_regions": [
    ],
  • "certifications": [
    ],
  • "version": 0,
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "published_at": "2019-08-24T14:15:22Z"
}

Discover agents (public)

query Parameters
q
string

Free-text search

category
string (CapabilityCategory)
Enum: "DOCUMENT_PROCESSING" "DATA_ANALYSIS" "COMMUNICATION" "FINANCIAL" "LEGAL" "COMPLIANCE" "IDENTITY" "INTEGRATION" "AUTOMATION" "CUSTOM"
min_trust_score
number [ 0 .. 1 ]
min_rating
number [ 1 .. 5 ]
pricing_model
string (PricingModel)
Enum: "FREE" "PER_CALL" "PER_MINUTE" "MONTHLY" "CUSTOM"
sort_by
string
Enum: "trust_score" "rating" "invocations" "response_time" "created_at"
sort_order
string
Enum: "asc" "desc"
cursor
string
limit
integer [ 1 .. 50 ]
Default: 20

Responses

Response samples

Content type
application/json
{
  • "listings": [
    ],
  • "next_cursor": "string",
  • "total_count": 0
}

Find best-matching agents for a task

Authorizations:
BearerAuth
Request Body schema: application/json
required
task_description
required
string

Natural language description of the task

required_capabilities
Array of strings

Specific capability IDs needed

max_results
integer [ 1 .. 20 ]
Default: 5

Responses

Request samples

Content type
application/json
{
  • "task_description": "string",
  • "required_capabilities": [
    ],
  • "max_results": 5
}

Response samples

Content type
application/json
{
  • "listings": [
    ],
  • "next_cursor": "string",
  • "total_count": 0
}

Submit a review

Authorizations:
BearerAuth
Request Body schema: application/json
required
listing_id
required
string
rating
required
integer [ 1 .. 5 ]
comment
string
capability_id
string
consent_request_id
string

Responses

Request samples

Content type
application/json
{
  • "listing_id": "string",
  • "rating": 1,
  • "comment": "string",
  • "capability_id": "string",
  • "consent_request_id": "string"
}

Response samples

Content type
application/json
{
  • "review_id": "string",
  • "listing_id": "string",
  • "agent_id": "string",
  • "reviewer_tenant_id": "string",
  • "rating": 1,
  • "comment": "string",
  • "capability_id": "string",
  • "consent_request_id": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

List reviews for a listing (public)

path Parameters
listingId
required
string
query Parameters
cursor
string
limit
integer [ 1 .. 50 ]
Default: 20

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string",
  • "total_count": 0
}

Delete own review

Authorizations:
BearerAuth
path Parameters
listingId
required
string
reviewId
required
string

Responses

Response samples

Content type
application/json
{
  • "error": "BAD_REQUEST",
  • "message": "principal and delegate are required"
}

Agent Registry

Agent Identity Registry — DID-based identity management for AI agents

Register a new AI agent identity

Creates a new agent identity with a DID, KMS-backed key pair, and initial ACTIVE status. The agent can then be bound to mandates.

Authorizations:
ApiKeyAuthBearerAuth
Request Body schema: application/json
required
name
required
string

Human-readable agent name

description
string

Agent description

algorithm
string
Default: "ES256"
Enum: "ES256" "ES384" "EdDSA"

Key algorithm for the agent's verification method

object (ClientPublicKeyJwk)

Bring-your-own-key registration: the agent generates its key pair locally and registers only the public half. Required for any agent that authenticates with private_key_jwt (e.g. POST /mcp/token) — a KMS-managed key has no signing API, so an external agent could never produce the client_assertion. When omitted, a KMS-managed key is provisioned for Mandaitor-hosted signing use cases instead.

object

Arbitrary metadata attached to the agent

environment
string <= 32 characters ^[a-z][a-z0-9-]{0,31}$

The environment the agent acts in, default when not given. Fixed at registration: an agent that must act in two environments is registered twice. Its executions use the tools enabled in this environment, which run on connectors of this environment only.

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "description": "string",
  • "algorithm": "ES256",
  • "public_key_jwk": {
    },
  • "metadata": { },
  • "environment": "qas"
}

Response samples

Content type
application/json
{
  • "agent_id": "string",
  • "tenant_id": "string",
  • "did": "string",
  • "name": "string",
  • "description": "string",
  • "status": "ACTIVE",
  • "verification_methods": [
    ],
  • "bound_mandates": [
    ],
  • "environment": "qas",
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

List registered agents

Returns a paginated list of agent identities for the authenticated tenant.

Authorizations:
ApiKeyAuthBearerAuth
query Parameters
status
string
Enum: "ACTIVE" "SUSPENDED" "REVOKED"
limit
integer [ 1 .. 200 ]
Default: 50
cursor
string

Base64url-encoded pagination cursor

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "cursor": "string"
}

Get agent details

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "agent_id": "string",
  • "tenant_id": "string",
  • "did": "string",
  • "name": "string",
  • "description": "string",
  • "status": "ACTIVE",
  • "verification_methods": [
    ],
  • "bound_mandates": [
    ],
  • "environment": "qas",
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Resolve agent DID Document

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/did+json
{ }

Rotate agent cryptographic key

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Request Body schema: application/json
algorithm
string
Default: "ES256"
Enum: "ES256" "ES384" "EdDSA"
object (ClientPublicKeyJwk)

Bring-your-own-key registration: the agent generates its key pair locally and registers only the public half. Required for any agent that authenticates with private_key_jwt (e.g. POST /mcp/token) — a KMS-managed key has no signing API, so an external agent could never produce the client_assertion. When omitted, a KMS-managed key is provisioned for Mandaitor-hosted signing use cases instead.

Responses

Request samples

Content type
application/json
{
  • "algorithm": "ES256",
  • "public_key_jwk": {
    }
}

Response samples

Content type
application/json
{
  • "agent_id": "string",
  • "tenant_id": "string",
  • "did": "string",
  • "name": "string",
  • "description": "string",
  • "status": "ACTIVE",
  • "verification_methods": [
    ],
  • "bound_mandates": [
    ],
  • "environment": "qas",
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Bind agent to a mandate

Lets the agent present a mandate in its MCP sessions. The mandate must be delegated to this agent: its delegate.subject_id is the agent's DID or agent id. A binding grants no authority of its own: /mcp/token issues a session only to an agent bound to a mandate that names it, and an execution is decided on the agent's active mandates, bound or not.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
mandate_id
required
string

Responses

Request samples

Content type
application/json
{
  • "mandate_id": "string"
}

Response samples

Content type
application/json
{
  • "agent_id": "string",
  • "tenant_id": "string",
  • "did": "string",
  • "name": "string",
  • "description": "string",
  • "status": "ACTIVE",
  • "verification_methods": [
    ],
  • "bound_mandates": [
    ],
  • "environment": "qas",
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Unbind agent from a mandate

MCP sessions the agent already holds stop using the mandate at once.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
mandateId
required
string

Responses

Response samples

Content type
application/json
{
  • "agent_id": "string",
  • "tenant_id": "string",
  • "did": "string",
  • "name": "string",
  • "description": "string",
  • "status": "ACTIVE",
  • "verification_methods": [
    ],
  • "bound_mandates": [
    ],
  • "environment": "qas",
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Suspend an agent

Ends every MCP session the agent holds, at once. The agent cannot obtain a token while suspended, and its earlier sessions stay dead after it is reactivated.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "agent_id": "string",
  • "tenant_id": "string",
  • "did": "string",
  • "name": "string",
  • "description": "string",
  • "status": "ACTIVE",
  • "verification_methods": [
    ],
  • "bound_mandates": [
    ],
  • "environment": "qas",
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Reactivate a suspended agent

The agent may request a new token from /mcp/token. Sessions issued before the suspension stay dead.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "agent_id": "string",
  • "tenant_id": "string",
  • "did": "string",
  • "name": "string",
  • "description": "string",
  • "status": "ACTIVE",
  • "verification_methods": [
    ],
  • "bound_mandates": [
    ],
  • "environment": "qas",
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Permanently revoke an agent

Revokes the agent's keys and ends every MCP session it holds. The agent can never obtain a token again.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "agent_id": "string",
  • "tenant_id": "string",
  • "did": "string",
  • "name": "string",
  • "description": "string",
  • "status": "ACTIVE",
  • "verification_methods": [
    ],
  • "bound_mandates": [
    ],
  • "environment": "qas",
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Billing

Pay-per-use billing, usage tracking, and payment management

Get usage data for a tenant

Returns usage data for the authenticated tenant's current or specified billing period. Includes total units, per-endpoint breakdown, daily usage timeline, and per-API-key breakdown. The tenant path parameter must match the authenticated caller.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
query Parameters
period
string^\d{4}-\d{2}$
Example: period=2026-04

Billing period in YYYY-MM format. Defaults to current month.

granularity
string
Default: "daily"
Enum: "daily" "hourly"

Responses

Response samples

Content type
application/json
{
  • "tenant_id": "string",
  • "billing_period": "2026-04",
  • "total_units": 0.1,
  • "endpoint_breakdown": {
    },
  • "daily_usage": [
    ],
  • "api_key_breakdown": [
    ],
  • "granularity": "daily",
  • "allowance": {
    }
}

Get billing status and invoice history

Returns the side-effect-free local billing projection for the authenticated tenant: the single approved metered offer, payment and entitlement state, recent invoices, and current-period usage. This GET never creates a Stripe Customer Portal session. The tenant path parameter must match the authenticated caller.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "tenant_id": "string",
  • "catalog_version": "string",
  • "offer": {
    },
  • "billing_status": "string",
  • "billing_plan": "metered",
  • "billing_plan_name": "string",
  • "entitlement_status": "active",
  • "activation_authority": "string",
  • "has_payment_method": true,
  • "stripe_connected": true,
  • "portal_available": true,
  • "subscription": {
    },
  • "invoices": [
    ],
  • "current_period": {
    }
}

Create a Stripe SetupIntent for card collection

Starts product-owned payment-method collection for the tenant when the rollout flags are enabled. A successful SetupIntent never activates paid access. Returns a client secret for Stripe.js / Stripe Elements.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "client_secret": "string",
  • "status": "pending",
  • "message": "string"
}

Start the single Mandaitor metered billing offer

Starts or resumes the single EUR 0.10 per 1,000 unit Mandaitor offer when rollout flags are enabled. The handler records a pending attempt; it never grants paid access. Activation is projected only after an eligible owned subscription and a paid or legitimately zero initial invoice. If payment details are missing, a SetupIntent client secret is returned without activating service.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
plan
required
string
Value: "metered"

The only approved Mandaitor paid offer

Responses

Request samples

Content type
application/json
{
  • "plan": "metered"
}

Response samples

Content type
application/json
{
  • "plan": "metered",
  • "plan_name": "string",
  • "status": "active",
  • "requires_payment": true,
  • "requires_action": true,
  • "subscription_id": "string",
  • "client_secret": "string",
  • "payment_client_secret": "string",
  • "message": "string"
}

Stripe webhook endpoint

Receives and processes Stripe webhook events. Authentication is performed via Stripe signature verification (not API key or JWT). Handles invoice lifecycle events (payment succeeded/failed/finalized) and subscription changes (updated/deleted).

Request Body schema: application/json
required
object

Raw Stripe webhook event payload

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
{
  • "received": true
}

Reconcile product-owned Stripe Customers

Admin-only, dry-run-first Customer inventory. Every mapped Customer is retrieved read-only and checked against the exact Mandaitor product, stage, and opaque owner metadata. The default request never writes. { "apply": true } creates Customers only for unmapped tenants and is rejected unless BILLING_CUSTOMER_SYNC_APPLY_ENABLED=true. It never creates subscriptions and is not scheduled automatically.

Authorizations:
ApiKeyAuthBearerAuth
Request Body schema: application/json
optional
apply
boolean
Default: false

Request gated Customer creation for unmapped tenants

Responses

Request samples

Content type
application/json
{
  • "apply": false
}

Response samples

Content type
application/json
{
  • "mode": "dry_run",
  • "scanned": 0,
  • "verified_owned": 0,
  • "legacy_mapping_requires_review": 0,
  • "missing_customer": 0,
  • "deleted_customer": 0,
  • "metadata_mismatch": 0,
  • "remote_lookup_failed": 0,
  • "created": 0,
  • "failed": 0
}

Connectors

List the connector catalog

Enterprise systems Mandaitor can govern. Each catalog entry lists the registered tools that act through the connector and the fields a tenant must configure. The mock adapters once listed with configurable: false retired in execution roadmap E2.11.

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
{
  • "connectors": [
    ]
}

List configured connector instances

Authorizations:
BearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "connectors": [
    ]
}

Configure a new connector instance

Validates the configuration against the catalog entry, rejects instance URLs the SSRF guard would block, KMS-encrypts secret fields, and stores the instance with status unverified. Secret fields are write-only — they are never returned by any connector route.

Authorizations:
BearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
type
required
string

Catalog entry to configure (must be configurable)

name
required
string <= 100 characters
required
object

Field values keyed by the catalog entry's config_fields names. Secret fields (e.g. password) are KMS-encrypted at rest and never returned by the API.

environment
string <= 32 characters ^[a-z][a-z0-9-]{0,31}$

The environment the system belongs to, default when not given. Fixed at creation.

Responses

Request samples

Content type
application/json
{
  • "type": "servicenow",
  • "name": "ServiceNow PDI (dev209313)",
  • "config": {},
  • "environment": "qas"
}

Response samples

Content type
application/json
{
  • "connector_id": "con_01JX5T9K2M",
  • "tenant_id": "string",
  • "type": "servicenow",
  • "name": "string",
  • "config": {
    },
  • "sandbox_id": "sbx_01JX5T9K2M",
  • "environment": "qas",
  • "credentials_rotated_at": "2019-08-24T14:15:22Z",
  • "credentials_rotated_by": "string",
  • "status": "unverified",
  • "last_test_at": "2019-08-24T14:15:22Z",
  • "last_test_message": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Provision a hosted demo sandbox and configure a connector for it

Creates a Mandaitor-hosted emulation of the requested enterprise system (ServiceNow Table API or SAP S/4HANA OData), seeds it with the demo records, and configures a connector instance pointing at it — so the full governed loop can be exercised without a customer system.

The result is an ordinary connector: same storage, same KMS-encrypted credential, same test-connection probe, same governed tool calls. Only the target differs, and sandbox_id marks it as sandbox-backed. Sandboxes expire after 30 days and are deleted with their connector.

Authorizations:
BearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
type
required
string
Enum: "servicenow" "sap-s4hana"

Which enterprise system to emulate

name
string <= 100 characters

Optional display name; defaults to " (demo sandbox)"

environment
string <= 32 characters ^[a-z][a-z0-9-]{0,31}$

The environment the sandbox stands in for, default when not given

Responses

Request samples

Content type
application/json
{
  • "type": "servicenow",
  • "name": "string",
  • "environment": "qas"
}

Response samples

Content type
application/json
{
  • "connector_id": "con_01JX5T9K2M",
  • "tenant_id": "string",
  • "type": "servicenow",
  • "name": "string",
  • "config": {
    },
  • "sandbox_id": "sbx_01JX5T9K2M",
  • "environment": "qas",
  • "credentials_rotated_at": "2019-08-24T14:15:22Z",
  • "credentials_rotated_by": "string",
  • "status": "unverified",
  • "last_test_at": "2019-08-24T14:15:22Z",
  • "last_test_message": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Reset a sandbox-backed connector's data to its seeded state

Restores the hosted sandbox behind this connector to the records it was provisioned with, so a demo can be re-run from a clean slate. Rejected for connectors that point at a real system.

Authorizations:
BearerAuth
path Parameters
id
required
string
connectorId
required
string

Responses

Response samples

Content type
application/json
{
  • "status": "reset",
  • "connector": {
    }
}

Remove a connector instance

Authorizations:
BearerAuth
path Parameters
id
required
string
connectorId
required
string

Responses

Response samples

Content type
application/json
{
  • "error": "BAD_REQUEST",
  • "message": "principal and delegate are required"
}

Run a live connection test

Has the executor, the one function that decrypts a credential, make one read-only probe against the configured system with the stored credential (for ServiceNow: an authenticated Table API read of a single requested item). Updates the instance's status, last_test_at, and last_test_message with the outcome, and nothing else.

Authorizations:
BearerAuth
path Parameters
id
required
string
connectorId
required
string

Responses

Response samples

Content type
application/json
{
  • "connector": {
    },
  • "test": {
    }
}

Replace a connector's credential, probed first

Replaces the connector's credential in place (execution roadmap E2.10). The executor probes the system with the new credential first; it is stored only if the system accepts it, and only if the stored credential is still the one read before the probe. The connector keeps its id, so the tools enabled on it, approvals waiting on it and executions not yet dispatched carry on, with the new credential. Requires tenant:write.

Where the system allows two credentials at once, as an API key often does, add the new one there, replace it here, then remove the old one there: nothing fails in between. Where changing it at the system ends the old one at once, as a password change does, executions dispatched between that change and this call fail.

A sandbox's credential is minted by Mandaitor and never shown: send no body. The sandbox accepts the new secret beside the old one while it is probed, and only the new one once it is stored, so a ServiceNow sandbox credential shown at provisioning stops working.

Authorizations:
BearerAuth
path Parameters
id
required
string
connectorId
required
string
Request Body schema: application/json
optional
required
object
property name*
additional property
string [ 1 .. 512 ] characters

Responses

Request samples

Content type
application/json
{
  • "credentials": {
    }
}

Response samples

Content type
application/json
{
  • "connector": {
    },
  • "test": {
    }
}

Consent

Human-in-the-Loop consent approval flows with auto-approval policies

Create a consent request for human-in-the-loop approval

Authorizations:
ApiKeyAuthBearerAuth
Request Body schema: application/json
required
mandate_id
required
string

The mandate the call falls under.

agent_id
required
string

The agent that would make the call.

agent_did
string
required
object
reason
required
string

Why consent is needed, as the approver reads it.

amount
number

The amount at stake, when there is one.

currency
string
urgency
string
Enum: "LOW" "NORMAL" "HIGH" "CRITICAL"
ttl_seconds
integer

How long the request stays open. Default 3600.

Responses

Request samples

Content type
application/json
{
  • "mandate_id": "string",
  • "agent_id": "string",
  • "agent_did": "string",
  • "tool_call": {
    },
  • "reason": "string",
  • "amount": 0,
  • "currency": "string",
  • "urgency": "LOW",
  • "ttl_seconds": 0
}

Response samples

Content type
application/json
{
  • "consent_id": "string",
  • "tenant_id": "string",
  • "mandate_id": "string",
  • "agent_id": "string",
  • "agent_did": "string",
  • "approver_subject_id": "string",
  • "approver_display_name": "string",
  • "status": "PENDING",
  • "urgency": "LOW",
  • "tool_call": {
    },
  • "reason": "string",
  • "amount": 0,
  • "currency": "string",
  • "decision": {
    },
  • "execution": {
    },
  • "notification": {
    },
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "expires_at": "2019-08-24T14:15:22Z"
}

List pending consent requests for the authenticated user

The pending consent requests of the caller's own tenant that name the caller as approver, in one page of up to limit. An execution's approval past its expires_at is left out: it can no longer be decided.

Authorizations:
ApiKeyAuthBearerAuth
query Parameters
limit
integer
Default: 50

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "count": 0
}

Get a consent request by ID

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "consent_id": "string",
  • "tenant_id": "string",
  • "mandate_id": "string",
  • "agent_id": "string",
  • "agent_did": "string",
  • "approver_subject_id": "string",
  • "approver_display_name": "string",
  • "status": "PENDING",
  • "urgency": "LOW",
  • "tool_call": {
    },
  • "reason": "string",
  • "amount": 0,
  • "currency": "string",
  • "decision": {
    },
  • "execution": {
    },
  • "notification": {
    },
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "expires_at": "2019-08-24T14:15:22Z"
}

Approve or reject a consent request

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
decision
required
string
Enum: "APPROVED" "REJECTED"
comment
string

The approver's comment, kept with the decision.

Responses

Request samples

Content type
application/json
{
  • "decision": "APPROVED",
  • "comment": "string"
}

Response samples

Content type
application/json
{
  • "consent_id": "string",
  • "tenant_id": "string",
  • "mandate_id": "string",
  • "agent_id": "string",
  • "agent_did": "string",
  • "approver_subject_id": "string",
  • "approver_display_name": "string",
  • "status": "PENDING",
  • "urgency": "LOW",
  • "tool_call": {
    },
  • "reason": "string",
  • "amount": 0,
  • "currency": "string",
  • "decision": {
    },
  • "execution": {
    },
  • "notification": {
    },
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "expires_at": "2019-08-24T14:15:22Z"
}

List consent requests for a specific mandate

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
query Parameters
status
string
Enum: "pending" "approved" "rejected" "expired"
limit
integer
Default: 20
cursor
string

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "count": 0
}

Create an auto-approval policy

Authorizations:
ApiKeyAuthBearerAuth
Request Body schema: application/json
required
name
required
string

Human-readable policy name

description
string
required
Array of objects (ConsentPolicyRule)
priority
integer

Higher priority policies are evaluated first (default 0)

enabled
boolean
Default: true

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "description": "string",
  • "rules": [
    ],
  • "priority": 0,
  • "enabled": true
}

Response samples

Content type
application/json
{
  • "policy_id": "string",
  • "tenant_id": "string",
  • "name": "string",
  • "description": "string",
  • "rules": [
    ],
  • "priority": 0,
  • "enabled": true,
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

List consent policies for the tenant

Authorizations:
ApiKeyAuthBearerAuth
query Parameters
limit
integer
Default: 20
cursor
string

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string",
  • "total_count": 0
}

Get a consent policy by ID

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "policy_id": "string",
  • "tenant_id": "string",
  • "name": "string",
  • "description": "string",
  • "rules": [
    ],
  • "priority": 0,
  • "enabled": true,
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Update a consent policy

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
name
string
description
string
Array of objects (ConsentPolicyRule)
priority
integer
enabled
boolean

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "description": "string",
  • "rules": [
    ],
  • "priority": 0,
  • "enabled": true
}

Response samples

Content type
application/json
{
  • "policy_id": "string",
  • "tenant_id": "string",
  • "name": "string",
  • "description": "string",
  • "rules": [
    ],
  • "priority": 0,
  • "enabled": true,
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Delete a consent policy

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "error": "BAD_REQUEST",
  • "message": "principal and delegate are required"
}

Decisions

Preview. Request-bound decisions: /verify's successor for an integrator whose enforcement point performs an agent's operation itself

Decide one operation your enforcement point is about to perform

Preview. /verify's successor for an integrator whose own enforcement point performs an agent's operation. Ask before you act, with the tenant's API key (the verify scope), never from the agent: name the agent by its DID, the taxonomy business action, the one resource it acts on, and the facts you read from your own system. Mandaitor decides as it decides an execution: every ACTIVE mandate naming the agent counts, a DENY mandate wins, and an ALLOW mandate counts only if a person created or approved it and it names the action exactly. Its time window, max_amount, cumulative limits, amount_above and require_mfa are checked on your facts; a mandate that limits amounts needs an amount fact, as money.

ALLOW carries authorization: a JWS (typ mandaitor-authorization+jwt, PS256) signed with the key the issuer's DID document lists as #authorization-key-1. It is bound to the request's digest, to audience and to five minutes at most, never past the mandate's expiry. Redeem it with POST /decisions/{id}/redemption immediately before you act; act only if the redemption succeeds. It counts against the mandate's cumulative limits when it is given, whether or not it is redeemed.

REQUIRE_APPROVAL and DENY carry no authorization. A decision request raises no approval: when a person would have to approve, the request fails closed.

The request digest is the SHA-256, sha-256: and base64url, of the RFC 8785 form of {contract, tenant_id, agent, action, resource, facts, audience}; mandate_id and purpose are not part of it. The SDK computes it as decisionRequestDigest.

Every answer is recorded on the audit chain before it is given. When it cannot be, nothing is given: 503 AUDIT_UNAVAILABLE. Each answer counts 2 units against the monthly allowance, an ALLOW or not.

Authorizations:
ApiKeyAuthBearerAuth
Request Body schema: application/json
required
contract
required
string <= 64 characters
Value: "mandaitor.decision-request/v1"
agent
required
string <= 256 characters ^did:[a-z0-9]+:[\x21-\x7E]+$

The agent you act for, by its DID.

action
required
string <= 128 characters ^[a-z][a-z0-9_-]*(\.[a-z][a-z0-9_-]*)+$

A business action a taxonomy defines. Unknown actions are denied.

resource
required
string <= 512 characters ^[A-Za-z0-9._~-]+(:[A-Za-z0-9._~-]+)*(/[A-Za-...

The one record the operation acts on, in the taxonomies' syntax. No *, no {placeholder}: a request never names more than one record.

required
object <= 32 properties

What you read from your own system of record, never from the agent. Names are 1 to 64 lower-case letters, digits and _, starting with a letter. Each value is a string (at most 256 characters), a safe integer, a boolean, or money; amount, when given, is money. Every fact is bound into the request digest.

audience
required
string [ 1 .. 256 ] characters ^[\x21-\x7E]{1,256}$

Your enforcement point, as it names itself. The authorization is for it alone.

mandate_id
string <= 68 characters ^mdt_[A-Za-z0-9]{1,64}$

The mandate you believe covers the operation. A hint only.

purpose
string <= 500 characters

Recorded with the decision. Never used to decide.

Responses

Request samples

Content type
application/json
{
  • "contract": "mandaitor.decision-request/v1",
  • "agent": "did:web:agents.mandaitor.io:agt_01JZ8Y3S6N0F4K7Q2W9E5R1T8V",
  • "action": "procurement.requisition.release",
  • "resource": "procurement:plant:1000/requisition:10004711",
  • "facts": {
    },
  • "mandate_id": "string",
  • "purpose": "string"
}

Response samples

Content type
application/json
{
  • "contract": "mandaitor.decision/v1",
  • "decision": "ALLOW",
  • "code": "ALLOWED",
  • "reason": "string",
  • "request_digest": "string",
  • "event_id": "string",
  • "decided_at": "2019-08-24T14:15:22Z",
  • "mandate_id": "string",
  • "fact": "string",
  • "constraints": [
    ],
  • "approval_rules": [
    ],
  • "decision_id": "string",
  • "authorization": "string",
  • "expires_at": "2019-08-24T14:15:22Z"
}

Redeem an authorization, once, immediately before acting on it

Preview. Send the digest of the operation you are about to perform, computed by you, never copied from the answer. The redemption succeeds once, and only:

  • for the request the authorization was given for;
  • before its exp;
  • while the agent is active and has not been suspended or revoked since, and the mandate it rests on is active and unchanged since;
  • while no kill switch runs on the tenant, and none was requested since it was given.

Act only on a 200. Every 409 means do not act, including ALREADY_REDEEMED after a retry whose first answer was lost: ask for a new decision. Every redemption, refused or not, is recorded on the audit chain before it is answered; when it cannot be, the answer is 503 and the authorization is left as it was. Redemptions are not counted against the allowance.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string <= 30 characters ^dec_[0-9A-Z]{26}$

The decision's decision_id, the authorization's jti.

Request Body schema: application/json
required
request_digest
required
string <= 51 characters ^sha-256:[A-Za-z0-9_-]{43}$

The digest of the operation you are about to perform, computed by you.

Responses

Request samples

Content type
application/json
{
  • "request_digest": "string"
}

Response samples

Content type
application/json
{
  • "decision_id": "string",
  • "redeemed_at": "2019-08-24T14:15:22Z",
  • "event_id": "string"
}

Drift Monitor

@experimental Behavioural drift monitoring for delegated agents

List agents with drift summaries

@experimental Aggregates drift session data across all mandates to produce per-agent summaries including average/max drift scores, session counts, and last activity timestamps.

Authorizations:
ApiKeyAuthBearerAuth

Responses

Response samples

Content type
application/json
[
  • {
    }
]

List recent drift detection events

@experimental Queries the tenant-wide event timeline for DRIFT_DETECTED and DRIFT_RESOLVED events, enriched with the drift score from the event details. Results are newest-first and paginate via an opaque cursor; follow next_cursor until it is absent to walk the full drift history.

Authorizations:
ApiKeyAuthBearerAuth
query Parameters
limit
integer [ 1 .. 100 ]
Default: 20

Maximum number of events to return per page.

cursor
string

Opaque base64url cursor from a previous response's next_cursor.

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

List drift sessions for a specific agent

@experimental Queries the drift sessions table to retrieve all recent session windows for a given delegate, enriched with computed drift scores and mandate context.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
delegateId
required
string

The delegate subject ID.

query Parameters
hours
integer [ 1 .. 168 ]
Default: 24

Lookback window in hours.

Responses

Response samples

Content type
application/json
[
  • {
    }
]

EUDI Wallet

eIDAS 2.0 EUDI Wallet identity verification (OpenID4VP)

Initiate EUDI Wallet verification session

Creates a new OpenID4VP session for EUDI Wallet identity verification. Returns a QR code URI (cross-device) and deep link URI (same-device) that the user scans/opens with their EUDI Wallet.

Authorizations:
ApiKeyAuthBearerAuth
Request Body schema: application/json
optional
presentation_definition_id
string
Default: "mandaitor-pid-standard"
Enum: "mandaitor-pid-minimal" "mandaitor-pid-standard" "mandaitor-pid-delegation"

ID of the presentation definition to use

Responses

Request samples

Content type
application/json
{
  • "presentation_definition_id": "mandaitor-pid-minimal"
}

Response samples

Content type
application/json
{
  • "session_id": "eudi_01HXYZ...",
  • "status": "PENDING",
  • "request_uri": "http://example.com",
  • "qr_code_uri": "string",
  • "deep_link_uri": "string",
  • "expires_at": "2019-08-24T14:15:22Z"
}

Poll EUDI session status

Polls the status of an EUDI Wallet verification session. Returns the resolved identity when the session is completed.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
sessionId
required
string

Responses

Response samples

Content type
application/json
{
  • "session_id": "string",
  • "status": "PENDING",
  • "created_at": "2019-08-24T14:15:22Z",
  • "presentation_definition_id": "string",
  • "resolved_identity": {
    },
  • "verified_at": "2019-08-24T14:15:22Z",
  • "errors": [
    ]
}

Receive VP Token from EUDI Wallet

Receives the VP Token from the EUDI Wallet via direct_post response mode. This endpoint is called by the wallet after user consent. No authentication required as the wallet POSTs directly.

path Parameters
sessionId
required
string
Request Body schema:
required
vp_token
required
string

The VP Token (SD-JWT VC compact serialization)

presentation_submission
object

DIF Presentation Submission mapping

Responses

Request samples

Content type
{
  • "vp_token": "string",
  • "presentation_submission": { }
}

Response samples

Content type
application/json
{
  • "status": "COMPLETED",
  • "session_id": "string"
}

Serve OpenID4VP Authorization Request Object

Serves the signed JWT Authorization Request Object that the EUDI Wallet fetches after scanning the QR code. Returns the JWT directly with content type application/oauth-authz-req+jwt.

path Parameters
requestId
required
string

Responses

Response samples

Content type
application/json
{
  • "error": "BAD_REQUEST",
  • "message": "principal and delegate are required"
}

Events

Immutable audit trail

Get audit events for a mandate

Returns a paginated list of audit events for a specific mandate. Events form an immutable, hash-chained audit trail.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Example: mdt_01HXYZ...
query Parameters
limit
integer [ 1 .. 200 ]
Default: 50
cursor
string

Base64url-encoded pagination cursor

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Query all events for the tenant

Queries all audit events for the authenticated tenant, filtered by mandate_id. Supports optional event_type filtering and cursor-based pagination.

Authorizations:
ApiKeyAuthBearerAuth
query Parameters
mandate_id
required
string

Mandate ID to filter events by

event_type
string
Enum: "MANDATE_CREATED" "MANDATE_SUSPENDED" "MANDATE_REACTIVATED" "MANDATE_REVOKED" "VERIFICATION_ALLOWED" "VERIFICATION_DENIED" "ESCALATION_TRIGGERED"
limit
integer [ 1 .. 200 ]
Default: 50
cursor
string

Base64url-encoded pagination cursor

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Get a specific event by ID

Retrieves a single audit event by its ID. Requires the mandate_id query parameter to locate the event in the partition.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Example: evt_01HXYZ...
query Parameters
mandate_id
required
string

Mandate ID the event belongs to

Responses

Response samples

Content type
application/json
{
  • "event_id": "string",
  • "tenant_id": "string",
  • "mandate_id": "string",
  • "event_type": "MANDATE_CREATED",
  • "timestamp": "2019-08-24T14:15:22Z",
  • "actor": {
    },
  • "details": { },
  • "previous_event_hash": "string",
  • "event_hash": "string",
  • "kms_signature": "string",
  • "hash_algorithm": "SHA_256",
  • "signing_algorithm": "RSASSA_PSS_SHA_256"
}

List audit events for the tenant (all mandates)

Tenant-scoped audit feed used by the dashboard Overview / Recent Activity. Returns events in descending timestamp order across all of the tenant's mandates. For a per-mandate timeline use GET /events?mandate_id=… instead. The tenant path parameter must match the authenticated caller. Requires Cognito JWT.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
query Parameters
limit
integer [ 1 .. 200 ]
Default: 50
cursor
string

Opaque base64url cursor from a previous response's next_cursor.

event_type
string

Optional filter on event_type (applied after the tenant-scoped query).

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Export tenant audit events for SIEM ingestion

Exports a tenant-scoped audit event slice in descending timestamp order for SIEM, GRC, and offline audit tooling. The tenant path parameter must match the authenticated caller unless the caller is a platform admin. Requires the events:export permission, which is granted to tenant administrators and tenant auditors.

The format parameter selects the interchange representation:

  • json (default) — Mandaitor's native envelope with pagination metadata in the body.
  • xes — eXtensible Event Stream (IEEE 1849) for process mining (ProM, Celonis, Apromore, Signavio). One trace per mandate, ordered oldest-first, with the standard concept, time, org, lifecycle and identity extensions declared.
  • cloudevents — CNCF CloudEvents 1.0 JSON batch for event meshes (Knative, Event Grid, Kafka). Attribute names follow the specification's lowercase-alphanumeric constraint, so Mandaitor's snake_case fields appear as tenantid, mandateid and eventhash.

XES and CloudEvents have no envelope for pagination, so the cursor, export id and event count are returned as X-Mandaitor-* response headers instead. These headers are listed in Access-Control-Expose-Headers so browser clients can read them.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
query Parameters
format
string
Default: "json"
Enum: "json" "xes" "cloudevents"

Interchange format for the export. Case-insensitive. An unsupported value returns 400 rather than falling back to JSON, so a caller cannot mistake a downgraded payload for a successful export.

limit
integer [ 1 .. 500 ]
Default: 100

Maximum number of audit events to include in this export page.

cursor
string

Opaque base64url cursor from a previous response's next_cursor.

event_type
string

Optional filter on event_type for SIEM-specific routing or replay windows.

from_timestamp
string <date-time>

Inclusive lower bound for the audit-event timestamp sort key.

to_timestamp
string <date-time>

Inclusive upper bound for the audit-event timestamp sort key.

Responses

Response samples

Content type
{
  • "export_id": "audexp_dG50X2FjbWU6MjAyNi0wNS0xOVQxMjozMDowMFowMDBa",
  • "tenant_id": "string",
  • "generated_at": "2019-08-24T14:15:22Z",
  • "event_count": 0,
  • "items": [
    ],
  • "next_cursor": "string"
}

Evidence

Export a court-ready evidence pack

Exports a comprehensive, court-ready evidence pack for a mandate containing:

  • Mandate snapshot at export time
  • Hash-chained audit event log
  • SD-JWT proof tokens from verification events
  • Case log hash (SHA-256 fingerprint over canonical event chain)
  • Export manifest with requester attribution, coverage, and recomputable digests
  • Immutable EVIDENCE_PACK_EXPORTED audit event recorded for the export operation
  • Issuer DID document reference for independent verification

The case_log_hash is a deterministic SHA-256 digest computed over the canonical JSON representation of the event chain, enabling tamper detection.

The export_manifest binds the mandate snapshot, event chain, proof tokens, and export envelope to recomputable digests so offline reviewers can detect tampering in the export artifact.

The schema_version field allows forward-compatible evolution of the pack format.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Example: mdt_01HXYZ...
query Parameters
event_id
string

Scope the evidence pack to a specific verification event

Responses

Response samples

Content type
application/json
{
  • "schema_version": "1.2.0",
  • "export_timestamp": "2019-08-24T14:15:22Z",
  • "tenant_id": "string",
  • "mandate_id": "string",
  • "mandate_snapshot": { },
  • "event_chain": [
    ],
  • "event_count": 0,
  • "case_log_hash": "string",
  • "chain_integrity": {
    },
  • "issuer": {},
  • "proof_tokens": [
    ],
  • "crypto_metadata": {
    },
  • "export_manifest": {
    },
  • "metadata": {
    }
}

Execution

The execution platform — business actions, the tool registry, each tenant's execution policy, and executions

List the loaded taxonomies

Every taxonomy this deployment loads, with its business actions and resource patterns. Mandates name these actions, and every tool maps onto exactly one of them. package is the npm package a taxonomy comes from; it is not the taxonomy id with a prefix.

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
{
  • "taxonomies": [
    ]
}

List the tool registry

Every version of every tool definition. A tool is one operation on one kind of system, mapped to one business action. Definitions are code and change only through release; tenants enable a version on one of their connectors with PUT /tenants/{id}/tools/{toolId}.

A tool is available when a loaded taxonomy defines its business action. An unavailable tool cannot be enabled.

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
{
  • "tools": [
    ]
}

Get the tenant's execution policy

Which tool version runs on which of the tenant's connectors, in each of its environments. version rises by one on every change, in any environment; an approval's digest records it, so a change to the policy forces a fresh approval, in every environment. Before the first enablement, policy_id is null and version is 0.

Authorizations:
BearerAuth
path Parameters
id
required
string <= 128 characters

Responses

Response samples

Content type
application/json
{
  • "policy_id": "tpol_01J9EXAMPLE",
  • "version": 0,
  • "updated_at": "2019-08-24T14:15:22Z",
  • "updated_by": "string",
  • "tools": [
    ]
}

Enable a tool version on a connector, in one environment

Enables one version of a tool in one environment, on one of the tenant's connectors, replacing any version enabled there before, and raises the policy version. Requires tenant:write. The tool must be available, and the connector must exist, be of the tool's connector_type and be in the environment named, default when none is. An agent's executions use the tool as enabled in the agent's environment.

Authorizations:
BearerAuth
path Parameters
id
required
string <= 128 characters
toolId
required
string <= 128 characters
Request Body schema: application/json
required
version
required
integer >= 1
connector_id
required
string <= 68 characters ^con_[A-Za-z0-9]{1,64}$
environment
string <= 32 characters ^[a-z][a-z0-9-]{0,31}$

The environment to enable it in, default when not given. The connector must be in it.

Responses

Request samples

Content type
application/json
{
  • "version": 1,
  • "connector_id": "string",
  • "environment": "qas"
}

Response samples

Content type
application/json
{
  • "policy_id": "string",
  • "version": 1,
  • "tool": {
    }
}

Disable a tool in one environment

Disables the tool in one environment and raises the policy version. Requires tenant:write.

Authorizations:
BearerAuth
path Parameters
id
required
string <= 128 characters
toolId
required
string <= 128 characters
query Parameters
environment
string (Environment) <= 32 characters ^[a-z][a-z0-9-]{0,31}$
Example: environment=qas

The environment to disable it in, default when not given

Responses

Response samples

Content type
application/json
{
  • "error": "BAD_REQUEST",
  • "message": "principal and delegate are required"
}

List the tenant's executions

The tenant's executions, newest first, for a person of the tenant: the dashboard's Executions page (roadmap E1.11). Each is the execution as its agent reads it, and what it rested on: the agent, the record and the facts read from it, the approval it needed, and what the write did.

Requires tenant:read in the caller's own tenant, which a dashboard session has and an API key never does. An agent reads its own executions with GET /executions/{id} and its session token.

state keeps the executions in that state. Mandaitor filters after it reads, so a page can hold fewer than limit, even none, with a next_cursor still to follow; the list ends when there is none.

An execution waiting for an approval that has expired is listed as stored until it is read or swept, within a minute: its approval.expires_at is then in the past.

Authorizations:
BearerAuth
path Parameters
id
required
string <= 128 characters
query Parameters
state
string
Enum: "AWAITING_APPROVAL" "AUTHORIZED_NOT_EXECUTED" "EXECUTING" "UNKNOWN" "SUCCEEDED" "FAILED" "DENIED"
limit
integer [ 1 .. 50 ]
Default: 20
cursor
string <= 512 characters

The next_cursor of the page before. Another is refused.

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Read one of the tenant's executions

One execution, for a person of the tenant, authorised as the list is. Another tenant's execution, or an id that does not exist, is 404. An execution whose approval expired undecided is answered, and recorded, as DENIED with APPROVAL_EXPIRED, as it is when its agent reads it.

Authorizations:
BearerAuth
path Parameters
id
required
string <= 128 characters
executionId
required
string <= 30 characters ^exe_[0-9A-Z]{26}$

Responses

Response samples

Content type
application/json
{
  • "contract": "mandaitor.execution/v1",
  • "execution_id": "string",
  • "state": "DENIED",
  • "state_reason": "NO_COVERING_MANDATE",
  • "tool": {
    },
  • "request_digest": "string",
  • "decision": {
    },
  • "approval": {
    },
  • "result": { },
  • "receipt": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "agent": {
    },
  • "connector_id": "string",
  • "arguments": { },
  • "purpose": "string",
  • "scope": {
    },
  • "facts": { },
  • "target": {
    },
  • "authorization": {
    },
  • "outcome": {
    },
  • "reconciliation": {
    },
  • "resolution": {
    },
  • "reservation": {
    },
  • "dispatched_at": "2019-08-24T14:15:22Z",
  • "completed_at": "2019-08-24T14:15:22Z"
}

Resolve an UNKNOWN execution

A person of the tenant records the outcome of an UNKNOWN execution that no read-back could establish (roadmap E2.1), after looking at the target system. Requires tenant:write in the caller's own tenant, and a person's session: an API key or an agent is refused with 403 NOT_A_PERSON.

The target's own record comes first. Mandaitor reads the record back once more before it records anything. If that read-back finds the write applied, the execution is SUCCEEDED; if it proves the write absent (and the target never acknowledged it), FAILED; either way with RESOLVED_BY_READ_BACK and a receipt, whatever you sent. When that differs from your outcome, the answer is 409 OUTCOME_CONTRADICTED, with the execution as it now is.

Otherwise your outcome stands, as SUCCEEDED or FAILED with RESOLVED_BY_OPERATOR, your evidence in resolution, and no receipt: Mandaitor signs only what it read back. Your resolution and evidence go on the audit trail. A FAILED execution tells its agent it may ask again, and a new request is a new write.

Authorizations:
BearerAuth
path Parameters
id
required
string <= 128 characters
executionId
required
string <= 30 characters ^exe_[0-9A-Z]{26}$
Request Body schema: application/json
required
outcome
required
string
Enum: "SUCCEEDED" "FAILED"

SUCCEEDED if the write took effect in the target system, FAILED if it did not. FAILED tells the agent it may ask again, which writes again: be sure.

evidence
required
string [ 1 .. 2000 ] characters

What you checked, and what you found. It goes on the audit trail.

transaction_ref
string [ 1 .. 256 ] characters

The target's own reference for the write, if you found one.

Responses

Request samples

Content type
application/json
{
  • "outcome": "SUCCEEDED",
  • "evidence": "ME53N shows 10000124 not released; its change log has no entry after 12:00",
  • "transaction_ref": "string"
}

Response samples

Content type
application/json
{
  • "contract": "mandaitor.execution/v1",
  • "execution_id": "string",
  • "state": "DENIED",
  • "state_reason": "NO_COVERING_MANDATE",
  • "tool": {
    },
  • "request_digest": "string",
  • "decision": {
    },
  • "approval": {
    },
  • "result": { },
  • "receipt": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "agent": {
    },
  • "connector_id": "string",
  • "arguments": { },
  • "purpose": "string",
  • "scope": {
    },
  • "facts": { },
  • "target": {
    },
  • "authorization": {
    },
  • "outcome": {
    },
  • "reconciliation": {
    },
  • "resolution": {
    },
  • "reservation": {
    },
  • "dispatched_at": "2019-08-24T14:15:22Z",
  • "completed_at": "2019-08-24T14:15:22Z"
}

Request an execution

An agent asks Mandaitor to perform one typed business operation, sent as a mandaitor.tool-request/v1. The agent names the tool and the record (arguments, identifiers only). The facts a decision turns on, such as the amount or the plant, are read from the target system, never taken from the agent.

Authenticated with the agent's session token from POST /mcp/token, bound to the agent's key, and nothing else: Authorization: DPoP mcp_… with a fresh DPoP proof for this request (McpSessionDPoP). A bearer session, API keys, user tokens, PoMs and consent ids are refused with 401. Each agent may send at most 30 requests a minute (429 RATE_LIMITED).

Idempotency. (tenant, agent, idempotency_key) names at most one execution, until 24 hours after it reaches a final state.

  • A repeat of the same request returns that execution (200).
  • The same key with a different request is 409 IDEMPOTENCY_CONFLICT.
  • A repeat while the first is still being decided is 409 IDEMPOTENCY_IN_PROGRESS, naming the execution. Retry after Retry-After.

purpose and mandate_id are not part of what makes two requests the same.

A denial is not an error. It is 201 with the execution in DENIED, recorded on the audit chain before it is returned. When the audit event cannot be written, nothing is created and the answer is 503 AUDIT_UNAVAILABLE.

Which mandates count. The decision reads every ACTIVE mandate naming the agent as its delegate, by its DID or its agent id. An ALLOW mandate counts only if it names the tool's business action exactly and was created or approved from a person's session. A DENY mandate counts whoever made it, and wins. More than 100 such mandates is DENIED with TOO_MANY_MANDATES: the decision never rests on part of them. mandate_id is a hint, and a hint at a mandate that does not count is DENIED with HINT_NOT_COVERING.

The record. The decision reads the record the arguments name from the target system, and decides on its facts: the mandate's limits, thresholds and validity, and the tool's preconditions. When the record cannot be read, nothing is decided: 503 TARGET_UNAVAILABLE.

An ALLOW is answered once it is recorded. The answer is 201 with the execution in AUTHORIZED_NOT_EXECUTED, and nothing is written yet. Mandaitor's worker then takes it from a queue, usually within seconds: it rechecks it against the mandates and the record as they are now, writes it once, and reads it back. Read the outcome with GET /executions/{id}: SUCCEEDED, FAILED, or UNKNOWN when the target's answer and the read-back leave it open. DENIED means the recheck refused it, and decision is then that refusal. Every step is recorded on the audit chain before it is taken, and a step that cannot be recorded is not taken: the execution waits in AUTHORIZED_NOT_EXECUTED, with nothing written, or in EXECUTING, and the worker takes it up again.

An UNKNOWN execution is read back again, a minute later and then less and less often, for about 41 hours. Nothing is ever written again. A read-back that finds the write applied, or proves it absent, makes it SUCCEEDED or FAILED with a receipt. When none does, it stays UNKNOWN until a person of the tenant resolves it.

A decision that needs a person's approval waits for it. The execution is answered in AWAITING_APPROVAL, with nothing written. approval names the consent in the approver's inbox, the approval digest the approver must send back, and when the approval expires (24 hours at most, never past the mandate's own expiry). The approver decides it with POST /executions/{id}/approval, and an approved execution is then rechecked, written and read back by the worker. An approval that expires undecided ends the execution in DENIED with APPROVAL_EXPIRED. An agent may raise 20 approval requests a day; past that a decision that needs one is DENIED with APPROVAL_LIMIT_REACHED: the excess is refused, not queued.

Phase 1. Executions run only against Mandaitor's hosted sandboxes: a tool whose connector points elsewhere is DENIED with CONNECTOR_NOT_PERMITTED.

Authorizations:
McpSessionDPoP
header Parameters
DPoP
required
string <= 4096 characters

A fresh DPoP proof (RFC 9449) for this request and this session token, signed by the key the session is bound to (McpSessionDPoP).

Request Body schema: application/json
required
contract
required
string
Value: "mandaitor.tool-request/v1"
tool
required
string [ 1 .. 128 ] characters
tool_version
integer >= 1

Pins a definition version. It must be the version the tenant enabled.

required
object

Identifiers only, checked against the tool's arguments_schema (GET /tools). Unknown keys are refused.

idempotency_key
required
string [ 8 .. 128 ] characters ^[\x21-\x7E]{8,128}$

8 to 128 visible ASCII characters, chosen by the agent.

mandate_id
string <= 68 characters ^mdt_[A-Za-z0-9]{1,64}$

The mandate the agent believes covers the call. A hint only: the decision considers every mandate that names the agent.

purpose
string <= 500 characters

Shown to an approver. Never used in the decision.

Responses

Request samples

Content type
application/json
{
  • "contract": "mandaitor.tool-request/v1",
  • "tool": "sap.purchase_requisition.release",
  • "tool_version": 1,
  • "arguments": {
    },
  • "idempotency_key": "6f1c2c8e-3b0e-4d5e-9d61-1d4a8f0b7c21",
  • "mandate_id": "string",
  • "purpose": "string"
}

Response samples

Content type
application/json
{
  • "contract": "mandaitor.execution/v1",
  • "execution_id": "string",
  • "state": "DENIED",
  • "state_reason": "NO_COVERING_MANDATE",
  • "tool": {
    },
  • "request_digest": "string",
  • "decision": {
    },
  • "approval": {
    },
  • "result": { },
  • "receipt": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Read one of the agent's executions

Authenticated like POST /executions. An agent reads only its own executions; another agent's or another tenant's execution is 404, as is an id that does not exist. An execution whose approval expired undecided is answered, and recorded, as DENIED with APPROVAL_EXPIRED.

This is how an agent learns the outcome of what it asked for: read it until it is final (DENIED, SUCCEEDED, FAILED) or waits for an approval. A few seconds between reads is enough; the SDK's waitForExecution does it.

Authorizations:
McpSessionDPoP
path Parameters
id
required
string <= 30 characters ^exe_[0-9A-Z]{26}$
header Parameters
DPoP
required
string <= 4096 characters

A fresh DPoP proof (RFC 9449) for this request and this session token, signed by the key the session is bound to (McpSessionDPoP).

Responses

Response samples

Content type
application/json
{
  • "contract": "mandaitor.execution/v1",
  • "execution_id": "string",
  • "state": "DENIED",
  • "state_reason": "NO_COVERING_MANDATE",
  • "tool": {
    },
  • "request_digest": "string",
  • "decision": {
    },
  • "approval": {
    },
  • "result": { },
  • "receipt": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Approve or reject an execution that waits for your approval

The person an execution's approval names decides it. Only a person's session is accepted: a dashboard session, or one from the tenant's own identity provider. An API key or an agent is refused with 403 NOT_A_PERSON, and anyone the approval does not name with 403 NOT_THE_APPROVER. When the mandate requires MFA, the session must have been signed in with it: every dashboard session is, and a session from the tenant's identity provider is when its token's amr says mfa (403 MFA_REQUIRED otherwise).

To approve, send the approval_digest you were shown. The approval is granted only when it equals the digest the approval was raised with and one computed again from a fresh read of the record, under the mandates and the execution policy as they are now. The answer is the execution in AUTHORIZED_NOT_EXECUTED, approved. Mandaitor's worker then rechecks it, writes it once and reads it back, as for a decision that needs no approval; the dashboard and GET /tenants/{id}/executions/{executionId} show the outcome.

When something changed, nothing is written:

  • The record, the mandates or the policy changed since the approval was raised, and the decision still needs an approval: a new one is raised, with its own consent and digest, and the answer is 409 DIGEST_MISMATCH carrying the execution with it.
  • The record changed so that it no longer needs an approval: the execution is DENIED with RECORD_CHANGED, and the agent asks again (409 DIGEST_MISMATCH).
  • The decision on the record as it is now is a denial, the mandate is no longer in force, the agent no longer active, or the tool no longer enabled: the execution is DENIED (409 EXECUTION_DENIED).

To reject, send REJECT; no digest is needed. The execution is DENIED with APPROVAL_REJECTED.

An approval is decided once. A second, concurrent decision is refused with 409 APPROVAL_IN_PROGRESS before it records anything.

Authorizations:
BearerAuth
path Parameters
id
required
string <= 30 characters ^exe_[0-9A-Z]{26}$
Request Body schema: application/json
required
decision
required
string
Enum: "APPROVE" "REJECT"
approval_digest
string^sha-256:[A-Za-z0-9_-]{43}$

Required to approve. The digest you were shown.

consent_id
string

The approval you were shown. One a newer approval replaced is refused.

comment
string <= 1000 characters

Responses

Request samples

Content type
application/json
{
  • "decision": "APPROVE",
  • "approval_digest": "string",
  • "consent_id": "string",
  • "comment": "string"
}

Response samples

Content type
application/json
{
  • "contract": "mandaitor.execution/v1",
  • "execution_id": "string",
  • "state": "DENIED",
  • "state_reason": "NO_COVERING_MANDATE",
  • "tool": {
    },
  • "request_digest": "string",
  • "decision": {
    },
  • "approval": {
    },
  • "result": { },
  • "receipt": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Governance

Aggregated oversight indicators for management roles — portfolio posture rather than individual mandates

Aggregated governance posture for the tenant

Aggregated governance indicators over a bounded window, for oversight roles rather than operators. Answers "is the delegation posture acceptable, and where is it worst" — a question the per-mandate surfaces cannot.

Rates carry their denominators. Every rate reports numerator and denominator, and value is null when the denominator is zero. A denial rate of zero from four checks and one from no checks are different statements; a bare 0 would conflate them.

Coverage is explicit. The read is bounded in both window length and index pages. When either bound truncates the read, coverage.truncated is true and a WINDOW_TRUNCATED caveat is present. Callers must not present figures as complete without checking it.

Caveats are part of the answer. They flag conditions that make an indicator misleading — no verifications in the window, a small latency sample, unassigned mandates, unscoreable risk profiles. A dashboard that renders the numbers and drops the caveats will mislead its reader.

Authorizations:
ApiKeyAuthBearerAuth
query Parameters
window_days
integer [ 1 .. 90 ]
Default: 30

Length of the reporting window in days. Rejected rather than clamped when out of range, so the response always answers the question that was asked.

Responses

Response samples

Content type
application/json
{
  • "tenant_id": "string",
  • "generated_at": "2019-08-24T14:15:22Z",
  • "coverage": {
    },
  • "boundary_adherence": {
    },
  • "human_oversight": {
    },
  • "response_latency": {
    },
  • "risk_distribution": {
    },
  • "org_breakdown": [
    ],
  • "caveats": [
    ]
}

Identity

Identity provider configuration, token exchange, and SCIM provisioning

Get tenant identity provider configuration

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "tenant_id": "string",
  • "identity_providers": {
    }
}

Update tenant identity provider configuration

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
enabled_providers
required
Array of strings
Items Enum: "API_KEY" "COGNITO" "EUDI_WALLET" "AUTH0" "OKTA" "ENTRA_ID" "GOOGLE" "AWS_IAM_IDC" "GENERIC_OIDC"
object
object
object
object
object
object
object

Responses

Request samples

Content type
application/json
{
  • "enabled_providers": [
    ],
  • "auth0": {
    },
  • "okta": {},
  • "entra": {
    },
  • "eudi": {
    },
  • "google": {
    },
  • "aws_iam_idc": {},
  • "generic_oidc": {}
}

Response samples

Content type
application/json
{
  • "tenant_id": "string",
  • "identity_providers": {
    }
}

List tenant role assignments

Returns the persisted role-assignment policy rows for a tenant. Each assignment binds a canonical subject ID to tenant-scoped roles and derived effective permissions. The tenant path parameter must match the authenticated caller unless the caller is a platform administrator. Requires tenant:read permission.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "tenant_id": "string",
  • "assignments": [
    ]
}

Create or update a tenant role assignment

Creates or replaces the tenant-scoped role assignment for the canonical subject ID in the path. platform_admin and platform:* grants are rejected because tenant policy can only delegate tenant-scoped access. Role-derived permissions and any explicit tenant permissions are stored as the assignment's effective permission set. Emits an immutable TENANT_ROLE_ASSIGNED audit event. Requires tenant:write permission.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
subjectId
required
string [ 3 .. 256 ] characters ^[A-Za-z0-9._:@/-]{3,256}$
Example: eudi:DE/1234567890abcdef

URL-encoded canonical subject ID resolved by the authorizer.

Request Body schema: application/json
required
subject_type
string
Default: "NATURAL_PERSON"
Enum: "NATURAL_PERSON" "LEGAL_ENTITY" "SERVICE" "AGENT"
display_name
string [ 1 .. 200 ] characters
email
string <email> <= 320 characters
roles
required
Array of strings (RbacRole) [ 1 .. 5 ] items
Items Enum: "platform_admin" "tenant_admin" "tenant_operator" "tenant_auditor" "tenant_viewer" "service"

Tenant-assignable RBAC roles. platform_admin is rejected for this endpoint.

permissions
Array of strings (RbacPermission) <= 30 items
Items Enum: "*" "platform:admin" "platform:onboarding:read" "platform:onboarding:write" "platform:tenants:read" "platform:tenants:write" "tenant:read" "tenant:write" "tenant:billing:read" "tenant:usage:read" "tenant:audit:read" "tenant:api-keys:read" "tenant:api-keys:write" "mandates:read" "mandates:write" "verify:execute" "events:read" "events:export" "evidence:read" "evidence:write"

Optional explicit tenant-scoped permissions merged with role-derived grants. Platform permissions are rejected.

Responses

Request samples

Content type
application/json
{
  • "subject_type": "NATURAL_PERSON",
  • "display_name": "Amina Bauer",
  • "email": "amina.bauer@example.com",
  • "roles": [
    ],
  • "permissions": [
    ]
}

Response samples

Content type
application/json
{
  • "tenant_id": "string",
  • "assignment": {
    }
}

Revoke a tenant role assignment

Revokes the tenant-scoped role assignment for the canonical subject ID by deleting the persisted policy row. The next authorizer evaluation no longer enriches the subject with those tenant roles or permissions. Emits an immutable TENANT_ROLE_REVOKED audit event. The operation is idempotent. Requires tenant:write permission.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
subjectId
required
string [ 3 .. 256 ] characters ^[A-Za-z0-9._:@/-]{3,256}$
Example: oidc:okta:00u123456789

URL-encoded canonical subject ID resolved by the authorizer.

Responses

Response samples

Content type
application/json
{
  • "error": "BAD_REQUEST",
  • "message": "principal and delegate are required"
}

Get current user's workspace entitlements

Returns the authenticated subject's workspace access for the current tenant context. If no explicit entitlement row exists, the response is derived from the tenant RBAC context: the Product Workspace is enabled by default, while the Partner & Project Workspace is enabled by default only for tenant administrators and platform administrators. Requires Cognito JWT or another authorizer-supported tenant principal.

Authorizations:
ApiKeyAuthBearerAuth

Responses

Response samples

Content type
application/json
{
  • "tenant_id": "tenant_demo",
  • "user_id": "eudi:DE/1234567890abcdef",
  • "workspaces": {
    },
  • "status": "ACTIVE",
  • "granted_by": "eudi:DE/admin-subject",
  • "granted_at": "2019-08-24T14:15:22Z",
  • "updated_by": "eudi:DE/admin-subject",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "source": "explicit"
}

Get workspace entitlements for a tenant user

Returns workspace access for the URL-encoded canonical subject ID in the current tenant. Reading another subject's entitlement requires tenant:read permission. If no explicit entitlement exists, default workspace access is resolved using the caller's tenant RBAC context.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
subjectId
required
string [ 3 .. 256 ] characters ^[A-Za-z0-9._:@/-]{3,256}$
Example: eudi:DE/1234567890abcdef

URL-encoded canonical subject ID resolved by the authorizer.

Responses

Response samples

Content type
application/json
{
  • "tenant_id": "tenant_demo",
  • "user_id": "eudi:DE/1234567890abcdef",
  • "workspaces": {
    },
  • "status": "ACTIVE",
  • "granted_by": "eudi:DE/admin-subject",
  • "granted_at": "2019-08-24T14:15:22Z",
  • "updated_by": "eudi:DE/admin-subject",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "source": "explicit"
}

Update workspace entitlements for a tenant user

Creates or replaces the explicit workspace entitlement row for the URL-encoded canonical subject ID in the current tenant. Setting both workspace flags to false stores the entitlement as REVOKED and causes both workspaces to be disabled in the response. Emits an immutable WORKSPACE_ENTITLEMENT_UPDATED audit event. Requires tenant:write permission.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
subjectId
required
string [ 3 .. 256 ] characters ^[A-Za-z0-9._:@/-]{3,256}$
Example: oidc:okta:00u123456789

URL-encoded canonical subject ID resolved by the authorizer.

Request Body schema: application/json
required
product_workspace
required
boolean

Grants access to the Product Workspace when the entitlement is active.

partner_project_workspace
required
boolean

Grants access to the Partner & Project Workspace when the entitlement is active.

Responses

Request samples

Content type
application/json
{
  • "product_workspace": true,
  • "partner_project_workspace": true
}

Response samples

Content type
application/json
{
  • "tenant_id": "tenant_demo",
  • "user_id": "eudi:DE/1234567890abcdef",
  • "workspaces": {
    },
  • "status": "ACTIVE",
  • "granted_by": "eudi:DE/admin-subject",
  • "granted_at": "2019-08-24T14:15:22Z",
  • "updated_by": "eudi:DE/admin-subject",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "source": "explicit"
}

Get workspace entitlements for a tenant user by tenant ID

Returns workspace access for the URL-encoded canonical subject ID within the tenant identified by the path parameter. Tenant-scoped callers may only use their own tenant ID unless the authorizer grants platform-admin privileges. Reading another subject's entitlement requires tenant:read permission. If no explicit entitlement exists, default workspace access is resolved using the caller's tenant RBAC context.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string [ 3 .. 128 ] characters ^[A-Za-z0-9._:-]{3,128}$
Example: tnt_mandaitor_053cb127

Tenant ID whose user workspace entitlements should be read.

subjectId
required
string [ 3 .. 256 ] characters ^[A-Za-z0-9._:@/-]{3,256}$
Example: oliver@mandaitor.io

URL-encoded canonical subject ID or email address recorded in tenant access assignments.

Responses

Response samples

Content type
application/json
{
  • "tenant_id": "tenant_demo",
  • "user_id": "eudi:DE/1234567890abcdef",
  • "workspaces": {
    },
  • "status": "ACTIVE",
  • "granted_by": "eudi:DE/admin-subject",
  • "granted_at": "2019-08-24T14:15:22Z",
  • "updated_by": "eudi:DE/admin-subject",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "source": "explicit"
}

Update workspace entitlements for a tenant user by tenant ID

Creates or replaces the explicit workspace entitlement row for the URL-encoded canonical subject ID within the tenant identified by the path parameter. Tenant-scoped callers may only use their own tenant ID unless the authorizer grants platform-admin privileges. Setting both workspace flags to false stores the entitlement as REVOKED and causes both workspaces to be disabled in the response. Emits an immutable WORKSPACE_ENTITLEMENT_UPDATED audit event. Requires tenant:write permission.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string [ 3 .. 128 ] characters ^[A-Za-z0-9._:-]{3,128}$
Example: tnt_mandaitor_053cb127

Tenant ID whose user workspace entitlements should be updated.

subjectId
required
string [ 3 .. 256 ] characters ^[A-Za-z0-9._:@/-]{3,256}$
Example: oliver@mandaitor.io

URL-encoded canonical subject ID or email address recorded in tenant access assignments.

Request Body schema: application/json
required
product_workspace
required
boolean

Grants access to the Product Workspace when the entitlement is active.

partner_project_workspace
required
boolean

Grants access to the Partner & Project Workspace when the entitlement is active.

Responses

Request samples

Content type
application/json
{
  • "product_workspace": true,
  • "partner_project_workspace": true
}

Response samples

Content type
application/json
{
  • "tenant_id": "tenant_demo",
  • "user_id": "eudi:DE/1234567890abcdef",
  • "workspaces": {
    },
  • "status": "ACTIVE",
  • "granted_by": "eudi:DE/admin-subject",
  • "granted_at": "2019-08-24T14:15:22Z",
  • "updated_by": "eudi:DE/admin-subject",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "source": "explicit"
}

Mandates

Core mandate lifecycle operations

Create a new mandate

Creates a new delegation mandate between a principal and a delegate. The mandate defines the scope of delegated authority, optional constraints, and an expiration date. A KMS-signed audit event is emitted on success.

Authorizations:
ApiKeyAuthBearerAuth
Request Body schema: application/json
required
required
object (Subject)

Represents an entity (human, AI agent, service, or organization) that participates in a mandate as either principal or delegate.

required
object (Subject)

Represents an entity (human, AI agent, service, or organization) that participates in a mandate as either principal or delegate.

required
object (Scope)

Defines the actions, resources, and effect (ALLOW/DENY) that a mandate authorizes, with optional conditions.

object (Constraints)

Optional restrictions on mandate usage.

Not every key here is enforced. POST /verify evaluates the mandate validity window (time), require_mfa, and escalation_rules.deviation_above_percent. The keys marked deprecated below are accepted and stored but are not evaluated — they do not restrict anything. The unsupported_constraints field on the creation response names any you send, and every verification response reports evaluated_constraints and unevaluated_constraints for the mandate that matched.

transaction_limits and rate_limits are the exceptions worth knowing: executions (POST /executions) enforce them, in the shapes the execution guide gives; POST /verify does not. In any other shape they restrict nothing, a mandate carrying them backs no execution, and unsupported_constraints names them.

These keys are deprecated as of 2026-09-10 and will be rejected at creation after the 90-day deprecation window for Stable endpoints. See the Beta Limitations page.

metadata
object
expires_at
string <date-time>

When the mandate stops authorizing; must be in the future. Enforced together with constraints.time.expires_at: the earlier wins.

taxonomy_id
string

Taxonomy identifier to validate the mandate scope against (e.g., "construction", "realestate", "venture").

taxonomy_version
string^[a-z][a-z0-9-]*(@\d+\.\d+\.\d+(-[a-z0-9.]+)?...

Versioned taxonomy reference. Accepts either a plain ID (uses latest version) or id@semver format (validates exact version match).

require_approval
boolean
Default: false

When true, the mandate starts in PENDING_APPROVAL status and requires explicit approval before becoming ACTIVE.

object (MandateIntent)

@experimental Intent declaration for drift detection. Allows the principal to express the intended purpose and expected behavioral clusters for a mandate. Used by the drift detection engine to compute semantic distance scores.

object (VUIRequest)

@experimental Verifiable User Instruction input. Captures the human's original instruction at mandate creation time. The instruction text will be encrypted at rest and its SHA-256 digest embedded into the Proof-of-Mandate Verifiable Credential.

Responses

Request samples

Content type
application/json
{
  • "principal": {
    },
  • "delegate": {
    },
  • "scope": {
    },
  • "constraints": {
    },
  • "metadata": { },
  • "expires_at": "2019-08-24T14:15:22Z",
  • "taxonomy_id": "construction",
  • "taxonomy_version": "construction@2.0.0",
  • "require_approval": false,
  • "intent": {
    },
  • "vui": {
    }
}

Response samples

Content type
application/json
{
  • "mandate_id": "mdt_01HXYZ...",
  • "tenant_id": "string",
  • "version": 0,
  • "status": "DRAFT",
  • "principal": {
    },
  • "delegate": {
    },
  • "scope": {
    },
  • "constraints": {
    },
  • "proof": {
    },
  • "metadata": { },
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "expires_at": "2019-08-24T14:15:22Z",
  • "vui": {
    },
  • "approval": {
    },
  • "unsupported_constraints": [
    ],
  • "unsupported_constraints_message": "string"
}

List mandates (paginated)

Returns a paginated list of mandates for the authenticated tenant. Supports optional filtering by status and cursor-based pagination.

Authorizations:
ApiKeyAuthBearerAuth
query Parameters
status
string
Enum: "DRAFT" "ACTIVE" "SUSPENDED" "REVOKED" "EXPIRED"
limit
integer [ 1 .. 200 ]
Default: 50
cursor
string

Base64url-encoded pagination cursor

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Get mandate by ID

Retrieves a single mandate by its ID. Returns the full mandate record including principal, delegate, scope, constraints, and proof metadata.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Example: mdt_01HXYZ...

Responses

Response samples

Content type
application/json
{
  • "mandate_id": "mdt_01HXYZ...",
  • "tenant_id": "string",
  • "version": 0,
  • "status": "DRAFT",
  • "principal": {
    },
  • "delegate": {
    },
  • "scope": {
    },
  • "constraints": {
    },
  • "proof": {
    },
  • "metadata": { },
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "expires_at": "2019-08-24T14:15:22Z",
  • "vui": {
    },
  • "approval": {
    }
}

Revoke a mandate

Permanently revokes a mandate. Only active or suspended mandates can be revoked. An optional reason can be provided. A KMS-signed audit event is emitted.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Example: mdt_01HXYZ...
Request Body schema: application/json
reason
string

Responses

Request samples

Content type
application/json
{
  • "reason": "Contract terminated"
}

Response samples

Content type
application/json
{
  • "mandate_id": "string",
  • "status": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Suspend a mandate

Temporarily suspends an active mandate. Suspended mandates can be reactivated later. An optional reason can be provided.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Example: mdt_01HXYZ...
Request Body schema: application/json
reason
string

Responses

Request samples

Content type
application/json
{
  • "reason": "Under review"
}

Response samples

Content type
application/json
{
  • "mandate_id": "string",
  • "status": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Reactivate a suspended mandate

Reactivates a previously suspended mandate, returning it to ACTIVE status. Only mandates in SUSPENDED state can be reactivated.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Example: mdt_01HXYZ...

Responses

Response samples

Content type
application/json
{
  • "mandate_id": "string",
  • "status": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Approve a pending mandate

Approves a mandate that is in PENDING_APPROVAL state, transitioning it to ACTIVE. Only mandates created with require_approval: true enter this state. An audit event (MANDATE_APPROVED) is emitted.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Example: mdt_01HXYZ...
Request Body schema: application/json
reason
string

Optional reason/notes for the approval

Responses

Request samples

Content type
application/json
{
  • "reason": "string"
}

Response samples

Content type
application/json
{
  • "mandate_id": "string",
  • "status": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Reject a pending mandate

Rejects a mandate that is in PENDING_APPROVAL state, transitioning it to REVOKED. An audit event (MANDATE_REJECTED) is emitted.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Example: mdt_01HXYZ...
Request Body schema: application/json
reason
string

Reason for rejection

Responses

Request samples

Content type
application/json
{
  • "reason": "string"
}

Response samples

Content type
application/json
{
  • "mandate_id": "string",
  • "status": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Retrieve the decrypted user instruction for a mandate

Returns the decrypted original user instruction that was provided when the mandate was created. Requires authentication and tenant ownership. An INSTRUCTION_REVEALED audit event is emitted on each access.

Only available for mandates that were created with a Verifiable User Instruction (VUI). Returns 404 if no instruction exists.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Example: mdt_01HXYZ...

Responses

Response samples

Content type
application/json
{
  • "instruction_text": "string",
  • "instruction_hash": "string",
  • "instruction_timestamp": "2019-08-24T14:15:22Z",
  • "instruction_channel": "chat",
  • "principal_confirmation": {
    },
  • "mandate_id": "string"
}

MCP Authorization

MCP Authorization Server — OAuth sessions for AI agents, for the MCP server and the execution API

MCP OAuth Authorization Server Metadata

The OAuth 2.0 Authorization Server Metadata (RFC 8414) of POST /mcp/token, where agents get their sessions. The same document is served at the domain's root, where RFC 8414 places it for this issuer and where MCP clients look for it: https://api.mandaitor.io/.well-known/oauth-authorization-server/v1.

Agents authenticate with the client credentials grant and their registry key (private_key_jwt). No browser flow exists yet: response_types_supported is empty, and the authorization_endpoint it names answers so.

Responses

Response samples

Content type
application/json
{
  • "issuer": "string",
  • "token_endpoint": "string",
  • "authorization_endpoint": "string",
  • "grant_types_supported": [
    ],
  • "token_endpoint_auth_methods_supported": [
    ],
  • "token_endpoint_auth_signing_alg_values_supported": [
    ],
  • "dpop_signing_alg_values_supported": [
    ],
  • "response_types_supported": [
    ],
  • "service_documentation": "string",
  • "scopes_supported": [
    ]
}

Exchange agent credentials for MCP access token

OAuth 2.0 client_credentials grant for MCP agents. The agent presents a signed JWT assertion and receives a scoped access token bound to its mandates. The parameters may be sent form-encoded, as OAuth clients send them, or as a JSON object.

The assertion (private_key_jwt) is signed with the agent's active registry key, which kid may name. It must carry iss and sub naming the agent (its DID), aud naming the authorization server — its issuer (https://api.mandaitor.io/v1) or this endpoint's URL — a unique jti of at most 256 characters, and an exp no more than five minutes ahead (a minute of clock skew is tolerated). Each assertion is accepted once: presenting the same jti again returns 401, so sign a new assertion for every token request. client_id may be left out, since the assertion's iss names the agent (RFC 7521); when both are sent, they must agree.

resource (RFC 8707) names what the token is for. The MCP server's URL (https://api.mandaitor.io/v1/mcp) gives a session that only the MCP server accepts. The API's base URL, or no resource, gives one that only the API's own routes accept. Anything else is 400 invalid_target.

A session bound to the agent's key (DPoP, RFC 9449). A DPoP header with a proof for this request, signed by a key the client holds, binds the session to that key: it is issued with token_type DPoP and lasts an hour, and every request with it must carry a fresh proof signed by the same key (McpSessionDPoP), so the token alone is of no use to anyone who takes it. /executions takes only such a session. Without a proof, the session is a bearer one (token_type Bearer) and lasts 15 minutes: the MCP server and GET /mcp/sessions/{id} take it, /executions does not.

header Parameters
DPoP
string <= 4096 characters

A DPoP proof (RFC 9449) for this request, asking for a session bound to its key: htm POST, htu this endpoint's URL, and no ath, since no token is presented yet. See McpSessionDPoP for what a proof is.

Request Body schema:
required
grant_type
required
string
Value: "client_credentials"
client_id
string

Agent DID. Optional, as the assertion's iss names the agent; when sent, it must agree.

client_assertion_type
required
string
Value: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
client_assertion
required
string

Signed JWT assertion, accepted once. Carries a unique jti and an exp no more than five minutes ahead.

scope
string

Space-separated mandate scopes

resource
string

What the token is for (RFC 8707): the MCP server's URL, or the API's base URL. A session is accepted only by the one it names; without a resource, by the API.

Responses

Request samples

Content type
No sample

Response samples

Content type
application/json
{
  • "access_token": "string",
  • "token_type": "DPoP",
  • "expires_in": 3600,
  • "scope": "string",
  • "session_id": "sess_0f3c9d2e8b7a6c5d4e3f2a1b0c9d8e7f"
}

OAuth authorization endpoint (no browser flow yet)

The authorization endpoint the server metadata names, because MCP clients refuse metadata without one. Mandaitor issues sessions only to registered agents, with the client credentials grant at POST /mcp/token. Signing in as a person through an MCP client is not available yet. Always 400 unsupported_response_type.

Responses

Response samples

Content type
application/json
{
  • "error": "invalid_target",
  • "error_description": "string"
}

Get MCP session details

Requires the session's own access token (Authorization: Bearer mcp_…, or DPoP mcp_… with a proof for a session bound to the agent's key); a token for a different session returns 404. So does a session whose agent has been suspended or revoked since it was issued.

Authorizations:
McpSessionAuthMcpSessionDPoP
path Parameters
id
required
string
header Parameters
DPoP
string <= 4096 characters

Required with a session bound to the agent's key, sent as Authorization: DPoP mcp_…: a fresh proof for this request and this token (McpSessionDPoP). Not sent with a bearer session.

Responses

Response samples

Content type
application/json
{
  • "session_id": "string",
  • "tenant_id": "string",
  • "agent_id": "string",
  • "agent_did": "string",
  • "mandate_ids": [
    ],
  • "created_at": "2019-08-24T14:15:22Z",
  • "expires_at": "2019-08-24T14:15:22Z",
  • "last_active_at": "2019-08-24T14:15:22Z",
  • "call_count": 0,
  • "cnf": {
    }
}

MCP Server

The MCP server (Streamable HTTP): an agent's MCP client lists the tools its mandates could back and calls them, each call an execution

Send one MCP message (Streamable HTTP)

The MCP server. The body is one JSON-RPC 2.0 message of the Model Context Protocol. The server speaks 2025-11-25, 2025-06-18 and 2025-03-26, and answers every request with one JSON object, never a stream. A batch is refused, and a notification gets 202 and no body.

  • initialize: the negotiated version, tools as the only capability, and instructions for the model.
  • ping.
  • tools/list: the tools the tenant enabled that a mandate naming the agent could back now, then the server's own get_execution. A tool's name is its id with each dot an underscore (sap_purchase_requisition_release).
  • tools/call: one execution, requested as POST /executions requests it, from the tool's arguments and an optional purpose for the approver. The call waits up to about 20 seconds for the execution to settle and answers with its outcome. One still running, or waiting for a person's approval, is answered with its state and id, for get_execution. While 50 calls are already waiting, across every agent, a call answers at once instead. A repeat of the same call by the same agent names the same execution while it runs, and for 10 minutes after it ends. A denial or a failure is a tool error (isError: true); an unknown or not enabled tool is a JSON-RPC error (-32602). The result's structuredContent is the execution as GET /executions/{id} returns it.

Authenticated with a session from POST /mcp/token issued for this server (its resource this URL): a bearer session, as stock MCP clients hold, or one bound to the agent's key, sent as DPoP with a proof for each request. Without one, the 401's WWW-Authenticate names the protected resource metadata (RFC 9728): https://api.mandaitor.io/.well-known/oauth-protected-resource/v1/mcp. A browser's request from an origin this API does not serve is refused with 403.

An agent may send 60 requests a minute, whatever they ask; past that, each is refused with 429 before anything is read. A tool call also counts against the agent's 30 executions a minute, and past those it is a tool error that says when to try again.

Authorizations:
McpSessionAuthMcpSessionDPoP
header Parameters
DPoP
string <= 4096 characters

Required with a session bound to the agent's key, sent as Authorization: DPoP mcp_…: a fresh proof for this request and this token (McpSessionDPoP). Not sent with a bearer session.

MCP-Protocol-Version
string
Enum: "2025-11-25" "2025-06-18" "2025-03-26"

The version negotiated at initialize, sent after it. Without it, 2025-03-26 is assumed.

Request Body schema: application/json
required
jsonrpc
required
string
Value: "2.0"
string or integer
method
string
object

Responses

Request samples

Content type
application/json
{
  • "jsonrpc": "2.0",
  • "id": "string",
  • "method": "tools/call",
  • "params": { }
}

Response samples

Content type
application/json
{
  • "jsonrpc": "2.0",
  • "id": "string",
  • "result": { },
  • "error": {
    }
}

Open a server-sent event stream (not offered)

Always 405. The server sends no messages of its own, so it offers no stream.

Responses

End an MCP session (not offered)

Always 405. The server keeps no MCP session, so there is none to end.

Responses

Onboarding

Tenant access request and activation

Submit an access request (no auth required)

Submits a new tenant access request. No authentication required. The request enters PENDING state and must be approved by an admin.

Request Body schema: application/json
required
company_name
required
string
contact_name
string
contact_email
required
string <email>
use_case
required
string
industry
string
Enum: "construction" "real_estate" "venture_capital" "other"
website
string <uri>
lead_source
string

Marketing or referral source associated with the beta request.

beta_track
string

Closed-beta routing segment used for the request.

asset
string

Specific public asset or CTA entry point that produced the request.

Responses

Request samples

Content type
application/json
{
  • "company_name": "example GmbH",
  • "contact_name": "Max Mustermann",
  • "contact_email": "max@example.com",
  • "use_case": "AI agent delegation for construction validation",
  • "industry": "construction",
  • "website": "http://example.com",
  • "lead_source": "trust-site",
  • "beta_track": "construction",
  • "asset": "home-hero"
}

Response samples

Content type
application/json
{
  • "request_id": "string",
  • "status": "PENDING",
  • "message": "string"
}

List API keys for the tenant (metadata only)

Returns metadata for every API key owned by the tenant. The raw key is only available at creation time via POST /tenants/{id}/api-keys; this endpoint never returns the raw key or its hash. The tenant is taken from the authenticated caller — the path parameter must match, or the request is rejected with 403. Requires Cognito JWT.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Generate a new API key for the tenant

Generates a new API key for a tenant. The raw key is returned only once in the response and cannot be retrieved again. Requires Cognito JWT.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Request Body schema: application/json
name
string
environment
string
Default: "live"
Enum: "live" "test"
scopes
Array of strings (ApiScope)
Items Enum: "mandates:read" "mandates:write" "verify"

Scope list the issued key will be allowed to exercise. Omitted or null means "all canonical scopes" — equivalent to passing ["mandates:read","mandates:write","verify"]. An empty array issues an inert key. Unknown scopes are rejected with 400.

Responses

Request samples

Content type
application/json
{
  • "name": "production-key",
  • "environment": "live",
  • "scopes": [
    ]
}

Response samples

Content type
application/json
{
  • "key_id": "string",
  • "api_key": "string",
  • "name": "string",
  • "environment": "live",
  • "scopes": [
    ],
  • "created_at": "2019-08-24T14:15:22Z"
}

Revoke an API key for the tenant

Flips the per-key row to status: "REVOKED". The authorizer reads per-key rows directly, so the key stops authenticating on the next request — other ACTIVE keys for the same tenant keep working. Idempotent for already-revoked keys. The tenant path parameter must match the authenticated caller. Requires Cognito JWT.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
keyId
required
string

Responses

Response samples

Content type
application/json
{
  • "error": "BAD_REQUEST",
  • "message": "principal and delegate are required"
}

Organisation

Organisational accountability structure — units, owners and risk classification for governance reporting

Create an organisational unit

Creates an accountability unit inside the tenant. Every unit names an accountable owner; the hierarchy is bounded to four levels so aggregated reporting stays readable.

The first unit created may omit parent_id and becomes the root. Any later unit must name an existing parent.

Authorizations:
ApiKeyAuthBearerAuth
Request Body schema: application/json
required
name
required
string
kind
required
string
Enum: "root" "division" "department" "team"
required
object (OrgUnitOwner)
parent_id
string

Required for every unit except the root

description
string
object

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "kind": "root",
  • "owner": {
    },
  • "parent_id": "string",
  • "description": "string",
  • "tags": {
    }
}

Response samples

Content type
application/json
{
  • "org_unit_id": "string",
  • "tenant_id": "string",
  • "parent_id": "string",
  • "kind": "root",
  • "name": "string",
  • "description": "string",
  • "owner": {
    },
  • "status": "ACTIVE",
  • "path": [
    ],
  • "depth": 3,
  • "tags": {
    },
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

List organisational units

Returns every unit of the tenant. The result is not paginated: the hierarchy is bounded by design and a management view needs the whole tree to render it.

Authorizations:
ApiKeyAuthBearerAuth
query Parameters
status
string
Enum: "ACTIVE" "ARCHIVED"

Restrict to active or archived units

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "total_count": 0
}

Get an organisational unit

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "org_unit_id": "string",
  • "tenant_id": "string",
  • "parent_id": "string",
  • "kind": "root",
  • "name": "string",
  • "description": "string",
  • "owner": {
    },
  • "status": "ACTIVE",
  • "path": [
    ],
  • "depth": 3,
  • "tags": {
    },
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Update an organisational unit

Updates descriptive fields and accountable ownership. kind, parent_id, path and depth are immutable: moving a unit changes the meaning of every historical aggregation that referenced it, so a move is modelled as archive plus create rather than an in-place edit.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
name
string
description
string
object (OrgUnitOwner)
object

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "description": "string",
  • "owner": {
    },
  • "tags": {
    }
}

Response samples

Content type
application/json
{
  • "org_unit_id": "string",
  • "tenant_id": "string",
  • "parent_id": "string",
  • "kind": "root",
  • "name": "string",
  • "description": "string",
  • "owner": {
    },
  • "status": "ACTIVE",
  • "path": [
    ],
  • "depth": 3,
  • "tags": {
    },
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Archive an organisational unit

Archives the unit. This is a soft delete: mandates and audit events keep referencing it, so hard deletion would break the evidence trail the platform exists to preserve.

A unit with active children cannot be archived — archiving a parent would leave its children unreachable in the tree while still active.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "org_unit_id": "string",
  • "tenant_id": "string",
  • "parent_id": "string",
  • "kind": "root",
  • "name": "string",
  • "description": "string",
  • "owner": {
    },
  • "status": "ACTIVE",
  • "path": [
    ],
  • "depth": 3,
  • "tags": {
    },
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Resolve a unit and everything below it

Returns the unit together with all descendants, resolved from the materialised ancestor path in a single read. This is the aggregation boundary for management reporting: "everything below this division".

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "root": {
    },
  • "items": [
    ],
  • "total_count": 0
}

Partner Workspace

Engagement workspace — knowledge catalog (media assets, documents, signed delivery) and per-phase engagement artifacts

List knowledge assets

Authorizations:
BearerAuth
query Parameters
limit
integer [ 1 .. 200 ]
Default: 50
cursor
string

Base64url-encoded pagination cursor

classification
string (MediaClassification)
Enum: "public" "partner-confidential" "client-confidential" "internal-restricted"
lifecycle_area
string
status
string (KnowledgeAssetStatus)
Enum: "planned" "draft" "release-candidate" "approved" "uploaded" "superseded" "archived" "withdrawn"

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Create or update a knowledge asset (expert catalog form)

Authorizations:
BearerAuth
Request Body schema: application/json
required
asset_id
string

Omit to create a new asset

title
required
string
classification
required
string (MediaClassification)
Enum: "public" "partner-confidential" "client-confidential" "internal-restricted"
lifecycle_area
required
string
status
string (KnowledgeAssetStatus)
Enum: "planned" "draft" "release-candidate" "approved" "uploaded" "superseded" "archived" "withdrawn"
lifecycle_phases
Array of strings
primary_audience
Array of strings
owner
string
source_references
Array of strings
manifest_path
string
current_version
string
storage_prefix
string
review_notes
string
required_reviewers
Array of strings
object (KnowledgeAssetReview)

Responses

Request samples

Content type
application/json
{
  • "asset_id": "string",
  • "title": "string",
  • "classification": "public",
  • "lifecycle_area": "string",
  • "status": "planned",
  • "lifecycle_phases": [
    ],
  • "primary_audience": [
    ],
  • "owner": "string",
  • "source_references": [
    ],
  • "manifest_path": "string",
  • "current_version": "string",
  • "storage_prefix": "string",
  • "review_notes": "string",
  • "required_reviewers": [
    ],
  • "review": {
    }
}

Response samples

Content type
application/json
{
  • "asset": {
    }
}

One-shot document publish (simplified flow)

Creates a single-file document asset from a title + file metadata, deriving all catalog fields server-side, and returns a presigned S3 PUT for the bytes. Send the returned upload_headers verbatim with the PUT — when a sha256 was announced, the checksum header is part of the signature and S3 rejects non-matching bytes. Finish with publish-complete.

Authorizations:
BearerAuth
Request Body schema: application/json
required
title
required
string
file_name
required
string
content_type
string
Default: "application/octet-stream"
size_bytes
required
integer <= 536870912
sha256
string

Optional 64-char hex digest; when present, S3 enforces it on upload

classification
string (MediaClassification)
Enum: "public" "partner-confidential" "client-confidential" "internal-restricted"
engagement_id
string

Links the document to an engagement via source_references

notes
string

Responses

Request samples

Content type
application/json
{
  • "title": "string",
  • "file_name": "string",
  • "content_type": "application/octet-stream",
  • "size_bytes": 536870912,
  • "sha256": "string",
  • "classification": "public",
  • "engagement_id": "string",
  • "notes": "string"
}

Response samples

Content type
application/json
{
  • "asset": {
    },
  • "version": {
    },
  • "file": {
    },
  • "upload_url": "string",
  • "upload_expires_in": 0,
  • "upload_headers": {
    }
}

Get a knowledge asset with its versions and files

Authorizations:
BearerAuth
path Parameters
assetId
required
string

Responses

Response samples

Content type
application/json
{
  • "asset": {
    },
  • "versions": [
    ]
}

Register an immutable media version (expert catalog form)

Version strings are immutable — re-posting an existing one answers 409. approved_by/approved_at are set server-side when status is approved and never accepted from the request. The asset's current_version only advances when the new version is genuinely newer (numeric dotted versions compare numerically).

Authorizations:
BearerAuth
path Parameters
assetId
required
string
Request Body schema: application/json
required
version
required
string

Immutable — an existing version string answers 409

status
string (MediaReviewStatus)
Enum: "draft" "approved" "uploaded" "superseded" "archived" "withdrawn"
storage_prefix
string
manifest_s3_key
string
manifest_path
string
release_notes
string
supersedes_version
string
source_commit
string
Array of objects

Responses

Request samples

Content type
application/json
{
  • "version": "string",
  • "status": "draft",
  • "storage_prefix": "string",
  • "manifest_s3_key": "string",
  • "manifest_path": "string",
  • "release_notes": "string",
  • "supersedes_version": "string",
  • "source_commit": "string",
  • "files": [
    ]
}

Response samples

Content type
application/json
{
  • "version": {
    }
}

Confirm a document upload landed and publish the asset

Verifies the uploaded object exists (and, when a SHA-256 was announced, that S3's stored checksum matches), records the real object size, and flips the asset and version to uploaded.

Authorizations:
BearerAuth
path Parameters
assetId
required
string

Responses

Response samples

Content type
application/json
{
  • "asset": {
    },
  • "version": {
    },
  • "file": {
    }
}

Create a short-lived signed download URL for a file

Authorizations:
BearerAuth
path Parameters
assetId
required
string
Request Body schema: application/json
required
version
string
file_id
string
relative_path
string
asset_id
string

Required on the /partner/media/files/{fileId}/signed-url form

project_id
string

Required for client-confidential assets — proves engagement scope

Responses

Request samples

Content type
application/json
{
  • "version": "string",
  • "file_id": "string",
  • "relative_path": "string",
  • "asset_id": "string",
  • "project_id": "string"
}

Response samples

Content type
application/json
{
  • "signed_url": "string",
  • "expires_in": 0,
  • "expires_at": "2019-08-24T14:15:22Z",
  • "file": {
    },
  • "asset": {
    },
  • "version": {
    }
}

Create a signed download URL addressing the file by id

Body must carry asset_id and version; the path's file id overrides any body file_id.

Authorizations:
BearerAuth
path Parameters
fileId
required
string
Request Body schema: application/json
required
version
string
file_id
string
relative_path
string
asset_id
string

Required on the /partner/media/files/{fileId}/signed-url form

project_id
string

Required for client-confidential assets — proves engagement scope

Responses

Request samples

Content type
application/json
{
  • "version": "string",
  • "file_id": "string",
  • "relative_path": "string",
  • "asset_id": "string",
  • "project_id": "string"
}

Response samples

Content type
application/json
{
  • "signed_url": "string",
  • "expires_in": 0,
  • "expires_at": "2019-08-24T14:15:22Z",
  • "file": {
    },
  • "asset": {
    },
  • "version": {
    }
}

List an engagement's artifacts

Authorizations:
BearerAuth
path Parameters
projectId
required
string

Responses

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Get one artifact

Answers 200 with "artifact": null when the artifact hasn't been authored yet — an expected state on fresh engagements, not an error. A missing engagement still answers 404.

Authorizations:
BearerAuth
path Parameters
projectId
required
string
artifactType
required
string (EngagementArtifactType)
Enum: "PILOT_CHARTER" "MANDATE_DESIGN" "VERIFICATION_PLAN" "EVIDENCE_PACK" "PILOT_GATE_DECISION" "OPERATIONS_HANDOVER"

Responses

Response samples

Content type
application/json
{
  • "artifact": {
    }
}

Create or update an artifact (bumps its version)

Authorizations:
BearerAuth
path Parameters
projectId
required
string
artifactType
required
string (EngagementArtifactType)
Enum: "PILOT_CHARTER" "MANDATE_DESIGN" "VERIFICATION_PLAN" "EVIDENCE_PACK" "PILOT_GATE_DECISION" "OPERATIONS_HANDOVER"
Request Body schema: application/json
required
required
object

Type-specific artifact payload (validated per artifact type)

property name*
additional property
any

Responses

Request samples

Content type
application/json
{
  • "payload": { }
}

Response samples

Content type
application/json
{
  • "artifact": {
    }
}

Submit an artifact for review

Authorizations:
BearerAuth
path Parameters
projectId
required
string
artifactType
required
string (EngagementArtifactType)
Enum: "PILOT_CHARTER" "MANDATE_DESIGN" "VERIFICATION_PLAN" "EVIDENCE_PACK" "PILOT_GATE_DECISION" "OPERATIONS_HANDOVER"
Request Body schema: application/json
optional
reviewer_role
string
notes
string

Responses

Request samples

Content type
application/json
{
  • "reviewer_role": "string",
  • "notes": "string"
}

Response samples

Content type
application/json
{
  • "artifact": {
    },
  • "review_task": {
    }
}

Public

Unauthenticated public endpoints

Health/status check

Returns service health status. No sensitive data is exposed. Suitable for monitoring, load balancers, and Trust Center embedding.

Responses

Response samples

Content type
application/json
{
  • "status": "ok",
  • "timestamp": "2019-08-24T14:15:22Z",
  • "version": "1.2.3-beta.1",
  • "stage": "prod"
}

Resolve the Mandaitor issuer DID document

Returns the W3C DID Document for the Mandaitor issuer DID (did:web:api.mandaitor.io). Contains the public key used to verify Proof-of-Mandate SD-JWT VCs.

Responses

Response samples

Content type
application/did+json
{
  • "@context": [
    ],
  • "id": "did:web:api.mandaitor.io",
  • "verificationMethod": [
    ],
  • "authentication": [
    ],
  • "assertionMethod": [
    ],
  • "service": [
    ]
}

Resolve the Mandaitor issuer DID document (spec-compliant path)

Spec-compliant resolution path for the issuer DID. The issuer DID is a path DID (did:web:api.mandaitor.io:v1), which per the W3C did:web specification resolves to https://api.mandaitor.io/v1/did.json — this endpoint. Returns the same W3C DID Document as /.well-known/did.json, which is kept as an alias.

Responses

Response samples

Content type
application/did+json
{
  • "@context": [
    ],
  • "id": "did:web:api.mandaitor.io:v1",
  • "verificationMethod": [
    ],
  • "authentication": [
    ],
  • "assertionMethod": [
    ],
  • "service": [
    ]
}

Public trust signals for machine-readable trust metadata

Returns machine-readable trust metadata including region, partition, issuer DID, data residency claims, and supported open standards. Unauthenticated and cache-friendly (5-minute client, 10-minute CDN).

Responses

Response samples

Content type
application/json
{
  • "schema_version": "1.0.0",
  • "region": "eusc-de-east-1",
  • "partition": "aws-eusc",
  • "issuer_did": "did:web:api.mandaitor.io",
  • "data_residency": {
    },
  • "open_standards": [
    ],
  • "build_version": "string",
  • "stage": "string",
  • "timestamp": "2019-08-24T14:15:22Z"
}

Submit an access request (no auth required)

Submits a new tenant access request. No authentication required. The request enters PENDING state and must be approved by an admin.

Request Body schema: application/json
required
company_name
required
string
contact_name
string
contact_email
required
string <email>
use_case
required
string
industry
string
Enum: "construction" "real_estate" "venture_capital" "other"
website
string <uri>
lead_source
string

Marketing or referral source associated with the beta request.

beta_track
string

Closed-beta routing segment used for the request.

asset
string

Specific public asset or CTA entry point that produced the request.

Responses

Request samples

Content type
application/json
{
  • "company_name": "example GmbH",
  • "contact_name": "Max Mustermann",
  • "contact_email": "max@example.com",
  • "use_case": "AI agent delegation for construction validation",
  • "industry": "construction",
  • "website": "http://example.com",
  • "lead_source": "trust-site",
  • "beta_track": "construction",
  • "asset": "home-hero"
}

Response samples

Content type
application/json
{
  • "request_id": "string",
  • "status": "PENDING",
  • "message": "string"
}

Public widget config for embed (no auth)

Returns the public-safe widget configuration for embedding. No authentication required. Sensitive fields (secrets, internal IDs) are stripped from the response.

path Parameters
widgetId
required
string

Responses

Response samples

Content type
application/json
{
  • "widget_id": "string",
  • "tenant_name": "string",
  • "branding": { },
  • "enabled_idps": [
    ],
  • "taxonomy_libraries": [
    ]
}

SCIM

SCIM 2.0 user provisioning (Okta/Entra ID lifecycle management)

List provisioned users

Authorizations:
ApiKeyAuthBearerAuth
query Parameters
startIndex
integer
Default: 1
count
integer
Default: 100

Responses

Response samples

Content type
application/scim+json
{ }

Provision a new user

Authorizations:
ApiKeyAuthBearerAuth
Request Body schema: application/scim+json
required
object

Responses

Request samples

Content type
application/scim+json
{ }

Response samples

Content type
application/scim+json
{ }

Get a provisioned user

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
userId
required
string

Responses

Response samples

Content type
application/json
{
  • "error": "BAD_REQUEST",
  • "message": "principal and delegate are required"
}

Replace a user

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
userId
required
string
Request Body schema: application/scim+json
required
object

Responses

Request samples

Content type
application/scim+json
{ }

Response samples

Content type
application/json
{
  • "error": "BAD_REQUEST",
  • "message": "principal and delegate are required"
}

Partially update a user

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
userId
required
string
Request Body schema: application/scim+json
required
object

Responses

Request samples

Content type
application/scim+json
{ }

Response samples

Content type
application/json
{
  • "error": "BAD_REQUEST",
  • "message": "principal and delegate are required"
}

Deprovision a user (revokes all mandates)

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
userId
required
string

Responses

Response samples

Content type
application/json
{
  • "error": "BAD_REQUEST",
  • "message": "principal and delegate are required"
}

Tenants

Get dashboard onboarding status

Returns the current dashboard onboarding tour status for the authenticated tenant. Used by the guided tour overlay to determine whether to show the onboarding flow.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
query Parameters
workspace
string
Enum: "product_workspace" "partner_project_workspace"

Workspace onboarding context. Defaults to product_workspace for backward compatibility.

Responses

Response samples

Content type
application/json
{
  • "tenant_id": "string",
  • "onboarding": {
    }
}

Update dashboard onboarding progress

Updates the dashboard onboarding tour progress for the authenticated tenant. Supports marking steps as completed, advancing the current step, and completing or skipping the entire tour.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
query Parameters
workspace
string
Enum: "product_workspace" "partner_project_workspace"

Workspace onboarding context. Defaults to product_workspace for backward compatibility.

Request Body schema: application/json
required
completed
boolean
skipped
boolean
completed_steps
Array of strings
current_step
string

Responses

Request samples

Content type
application/json
{
  • "completed": true,
  • "skipped": true,
  • "completed_steps": [
    ],
  • "current_step": "string"
}

Response samples

Content type
application/json
{
  • "tenant_id": "string",
  • "onboarding": {
    }
}

Verification

Real-time action authorization

Verify if an action is authorized by a mandate

Performance-critical endpoint. Target: < 50ms p99 latency (without PoM), < 200ms p99 (with PoM). Returns ALLOW or DENY with optional escalation metadata and an optional Proof-of-Mandate Verifiable Credential.

Authorizations:
ApiKeyAuthBearerAuth
query Parameters
pom
string
Value: "sd-jwt-vc"

Request a Proof-of-Mandate Verifiable Credential in the response. The VC is an SD-JWT signed by the Mandaitor issuer DID.

drift
boolean
Default: false

@experimental Request drift detection signals in the response. When true, the response includes a semantic_signals object with multi-dimensional drift scoring. Drift signals are advisory only and NEVER affect the ALLOW/DENY decision. Requires the tenant to have drift detection enabled.

Request Body schema: application/json
required
delegate_subject_id
string [ 1 .. 512 ] characters

Canonical field. Subject ID of the delegate to verify.

agent_id
string [ 1 .. 512 ] characters
Deprecated

Deprecated legacy alias for delegate_subject_id. Use delegate_subject_id for new integrations.

action
required
string [ 1 .. 256 ] characters
resource
required
string [ 1 .. 256 ] characters
context
object

Additional context for constraint evaluation. When a mandate has require_mfa: true, the caller must supply MFA proof via one of:

  • amr (array of strings) — must include "mfa"
  • loa (string) — must be "SUBSTANTIAL" or "HIGH"
  • mfa_timestamp (string, ISO 8601) — timestamp of last MFA verification

Responses

Request samples

Content type
application/json
{
  • "delegate_subject_id": "construction:agent:validate-agent-v2",
  • "agent_id": "construction:agent:validate-agent-v2",
  • "action": "construction.validation.approve",
  • "resource": "construction:project:proj_ABC123/zone:EG/installation:stk_42",
  • "context": { }
}

Response samples

Content type
application/json
{
  • "decision": "ALLOW",
  • "mandate_id": "string",
  • "event_id": "string",
  • "reason_codes": [
    ],
  • "constraints_remaining": { },
  • "evaluated_constraints": [
    ],
  • "unevaluated_constraints": [
    ],
  • "proof_of_mandate": {
    },
  • "proof_token": "string",
  • "semantic_signals": {
    }
}

Playground dry-run verify (Cognito JWT, no raw API key)

Dashboard-only dry-run verify endpoint. Authenticated via Cognito JWT (dashboard session). The caller selects an API key by its key_id from a dropdown; the handler loads the key's metadata (scopes, status) and simulates the verify flow as if the request had come through that key. No raw API key is ever needed. Metering is intentionally skipped.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
key_id
required
string

API key ID to simulate (from the dropdown)

action
required
string

The action being attempted

resource
required
string

The resource being accessed

delegate_subject_id
string

The delegate attempting the action

agent_id
string

Alternative to delegate_subject_id

object

Additional context for constraint evaluation

request_pom
boolean

Whether to include a Proof-of-Mandate VC

Responses

Request samples

Content type
application/json
{
  • "key_id": "string",
  • "action": "string",
  • "resource": "string",
  • "delegate_subject_id": "string",
  • "agent_id": "string",
  • "context": { },
  • "request_pom": true
}

Response samples

Content type
application/json
{
  • "decision": "ALLOW",
  • "mandate_id": "string",
  • "event_id": "string",
  • "reason_codes": [
    ],
  • "constraints_remaining": { },
  • "proof_of_mandate": { },
  • "proof_token": "string",
  • "_playground": {
    }
}

VI Credential

Verifiable Intent (VI) compatibility layer for cross-platform credential exchange

Issue a Verifiable Intent credential chain

Generates a VI-spec compatible credential chain (L1 + L2) from an existing active mandate. The chain can be presented to external verifiers that implement the Verifiable Intent specification.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Example: mdt_01HXYZ...
Request Body schema: application/json
required
audience
required
string

Target verifier audience URI

nonce
string

Optional nonce for replay protection

agent_public_key
object

Agent's JWK public key for L2 credential

principal_public_key
object

Principal's JWK public key for L1 credential

principal_key_id
string

KMS key ID for principal signing

Responses

Request samples

Content type
application/json
{
  • "audience": "string",
  • "nonce": "string",
  • "agent_public_key": { },
  • "principal_public_key": { },
  • "principal_key_id": "string"
}

Response samples

Content type
application/json
{
  • "credential_chain": {
    },
  • "format": "vi-sd-jwt",
  • "spec_version": "1.0"
}

Widget Config

Tenant widget configuration

Get tenant widget configuration

Retrieves the widget configuration for a tenant. Returns the latest version by default, or a specific version if the version query parameter is provided.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "tenant_id": "string",
  • "widget_id": "string",
  • "identity_providers": [
    ],
  • "taxonomy_libraries": [
    ],
  • "mandate_templates": [
    ],
  • "branding": {
    },
  • "approval_workflow": {
    },
  • "webhook_url": "http://example.com",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Update tenant widget configuration

Updates the widget configuration for a tenant. Creates a new versioned record. IdP credentials are stored in AWS Secrets Manager.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
identity_providers
Array of objects
taxonomy_libraries
Array of strings
mandate_templates
Array of objects
branding
object
approval_workflow
object
webhook_url
string <uri>

Responses

Request samples

Content type
application/json
{
  • "identity_providers": [
    ],
  • "taxonomy_libraries": [
    ],
  • "mandate_templates": [
    ],
  • "branding": { },
  • "approval_workflow": { },
  • "webhook_url": "http://example.com"
}

Response samples

Content type
application/json
{
  • "tenant_id": "string",
  • "widget_id": "string",
  • "identity_providers": [
    ],
  • "taxonomy_libraries": [
    ],
  • "mandate_templates": [
    ],
  • "branding": {
    },
  • "approval_workflow": {
    },
  • "webhook_url": "http://example.com",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Validate an IdP connection

Validates an Identity Provider configuration by attempting a client credentials flow against the specified provider (Entra ID, Auth0, Okta, or eIDAS).

Alias support: Accepts both provider (canonical) and idpType (legacy). If both are present, provider takes precedence.

Authorizations:
ApiKeyAuthBearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
provider
string
Enum: "eidas_eudi" "entra_id" "auth0" "okta"

Canonical field. The IdP type to validate.

idpType
string
Deprecated

Deprecated legacy alias for provider.

config
required
object

Responses

Request samples

Content type
application/json
{
  • "provider": "eidas_eudi",
  • "idpType": "string",
  • "config": { }
}

Response samples

Content type
application/json
{
  • "results": {
    }
}

List widget configuration versions

Returns a paginated list of all configuration versions for the tenant, sorted by version number descending (newest first). Each entry contains version metadata (not the full configuration body).

Authorizations:
BearerAuth
path Parameters
id
required
string
query Parameters
limit
integer <= 100
Default: 20
cursor
string

Responses

Response samples

Content type
application/json
{
  • "versions": [
    ],
  • "next_cursor": "string"
}

Rollback widget configuration to a previous version

Rolls back the widget configuration to a previous version by creating a new version that is a copy of the target version. This preserves the full version history for audit purposes — no versions are deleted.

The new version includes a rollbackFrom field indicating which version it was copied from.

Authorizations:
BearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
version
required
integer >= 1

The version number to roll back to

Responses

Request samples

Content type
application/json
{
  • "version": 1
}

Response samples

Content type
application/json
{
  • "message": "string",
  • "version": 0,
  • "rollbackFrom": 0,
  • "widgetId": "string"
}