ID MANDAT

API reference

Verify API, version 0.3.0-beta · read-only · no sign-in · free

Ask ID MANDAT who stands behind a domain and which AI agents act for it. The answer is JSON, optionally with a digital signature (Ed25519). This page describes what the interface does today. Declarations of the operator (“may”, “mayNot”, limits, countries) are not checked or enforced by ID MANDAT. ID MANDAT confirms identity and authority, not creditworthiness or quality.

Endpoints

RequestPurpose
GET /api/v1/verify?domain=example.comEntry and level for a domain. Optional &agent=Name narrows the result to one agent.
GET /api/v1/verify?signature_agent=https://signer.example.comLooks up who declared this key directory (Web Bot Auth, header Signature-Agent). The header value itself also works.
GET /api/v1/keysPublic keys for checking signatures (Ed25519, base64url without padding). Fields: key_id, public_key, status (active / retired).

Methods: GET, HEAD, OPTIONS. CORS is open (Access-Control-Allow-Origin: *). Base URL: https://idmandat.com.

Response fields

FieldMeaning
levelverified, registered, listed_unverified or none. See below.
level_textPlain-language explanation of the level.
entry_stateOnly if an earlier entry is no longer valid: revoked, suspended or expired. Then level is none. Do not rely on earlier answers.
organization, country, city, industryCompany details as listed.
agents[]Agents of the company with may, mayNot, optional limits (per transaction / per day, with currency) and validIn (countries). All declared by the operator.
agent_foundWith &agent=: whether an agent of that name exists on the entry.
mandate_sourceAlways declared_by_operator.
registered_at, last_checkedEntry date and last automatic re-check.
signed, key_id, issued_at, expires_at, signatureSignature data. If signed is false, the answer must not be treated as proof.
api, version, betaName and version of this interface.

Levels

Errors

StatuserrorCause
400invalid_domaindomain missing or not a valid domain.
400invalid_signature_agentsignature_agent is not a usable address.
405method_not_allowedAnything other than GET, HEAD, OPTIONS.

Error bodies are JSON with error and message. An unknown domain is not an error: you get status 200 with "registered": false.

Limits, caching and validity

Verifying the signature

  1. Take the answer without the field signature.
  2. Sort all object keys recursively in alphabetical order and write the JSON compactly (no spaces; do not escape Unicode or “/”).
  3. Check the UTF-8 bytes of that text against signature.value with the public key whose key_id matches (Ed25519, base64url without padding).
  4. Check that expires_at has not passed.

Example in Python (needs the package cryptography):

import base64, json
from datetime import datetime, timezone
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey

def b64u(s):
    return base64.urlsafe_b64decode(s + "=" * (-len(s) % 4))

def kanonisch(x):
    # Objektschlüssel rekursiv sortieren, kompakt, UTF-8, "/" und Unicode nicht escapen
    return json.dumps(x, sort_keys=True, separators=(",", ":"), ensure_ascii=False)

def pruefen(antwort, schluessel):
    if not antwort.get("signed"):
        return False                       # nicht signiert: kein Nachweis
    ablauf = datetime.strptime(antwort["expires_at"], "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc)
    if datetime.now(timezone.utc) > ablauf:
        return False                       # abgelaufen: neu abrufen
    key = next(k for k in schluessel["keys"] if k["key_id"] == antwort["key_id"])
    ohne = {k: v for k, v in antwort.items() if k != "signature"}
    try:
        Ed25519PublicKey.from_public_bytes(b64u(key["public_key"])).verify(
            b64u(antwort["signature"]["value"]), kanonisch(ohne).encode("utf-8"))
        return True
    except Exception:
        return False

Versioning

Every answer names the version of the interface in version (currently 0.3.0-beta). The interface is in beta. Read fields by name and ignore fields you do not know.

Related

FAQ · The standard · MCP server: https://idmandat.com/mcp · A2A card: https://idmandat.com/.well-known/agent-card.json