Skip to content
DID.is
Build

Developer center

Two surfaces, one resolver: the W3C DID Resolution HTTPS binding for interoperability, and the DID.is evidence API for everything a verifier needs to explain its decision. Free and anonymous to start; no key required.

Quickstartnpm install @didis/clientpip install didis-pyhttps://did.is/api
Samples
Guide

Quickstart

The public API needs no account and no key. Resolve any supported identifier and you get the W3C result plus the evidence behind it: a plain-language verdict, independently evaluated dimensions, and the stated limits of what was checked.

  1. Install the SDK for your language, or use any HTTP client.
  2. Resolve an identifier and read verdict.headline and dimensions.
  3. Branch on the stable problem code when a request fails, and honour Retry-After.
  4. When you need metered private resolution, monitoring or signed webhooks, create a project in the console.

Supported methods: did:web, did:webvh, did:key, did:jwk. No scores, no paid ranking: the response is identical for every caller.

1 · Install
# Nothing to install.
2 · Resolve
curl -s "https://did.is/api/v1/resolve/did%3Aweb%3Aidentity.foundation"
Guide

SDKs & CLI

Official, MIT-licensed clients with typed responses for resolution, credentials, policies, agents and fast verify. Both default to https://did.is/api; pass another base URL to target a staging or self-hosted deployment.

TypeScript · npm
npm install @didis/client

import { DidisClient } from "@didis/client";
const didis = new DidisClient();
const r = await didis.resolve("did:web:example.com");

Node 18+, browsers and edge runtimes (fetch + WebCrypto). · npm · source

Python · PyPI
pip install didis-py

from didis import DidisClient
client = DidisClient()
r = client.resolve("did:web:example.com")

Python 3.11+, sync and asyncio (AsyncDidisClient), TypedDict responses. · PyPI · source

Any language · HTTP
curl -s "https://did.is/api/v1/fast-verify/did/did%3Aweb%3Aexample.com"

# W3C DID Resolution HTTPS binding
curl -s -H 'Accept: application/did-resolution' \
  "https://did.is/api/1.0/identifiers/did%3Aweb%3Aexample.com"

Plain JSON over HTTPS. Resolver clients that speak the W3C HTTPS binding can use the /1.0/identifiers binding unchanged.

Typed errors

DidisError carries the RFC 9457 problem, status and Retry-After.

Live stages

stream() yields each resolution stage over SSE as it completes.

Webhook helpers

Constant-time signature verification for monitor events.

Timeouts

30 s default per request; configurable in both SDKs.

Guide

Base URL & encoding

  • Base URL. All paths below are relative to https://did.is/api. HTTPS only.
  • Identifiers in paths. Send the DID raw or fully percent-encoded (did%3Aweb%3A…); it is decoded exactly once. Encode # as %23 and ? as %3F in DID URLs. The SDKs do this for you.
  • Content types. Responses are application/json; failures are application/problem+json; the W3C binding negotiates application/did-resolution or application/did.
  • Bodies. POST bodies are JSON, at most 512 KiB.
  • OpenAPI. The full public surface is described as OpenAPI 3.1 at https://did.is/api/openapi.json. Import it into Postman, Insomnia or a client generator; it is built from this page's own catalogue.
  • Privacy. Public resolutions are observed and may appear in the identifier's public history. Use the private API for identifiers you must not disclose.
Encoding a DID URL
# did:web:example.com#key-1
curl -s "https://did.is/api/v1/dereference/did%3Aweb%3Aexample.com%23key-1"
Guide

Rate limits

The public API is free and anonymous, protected by per-client token buckets. Exceeding a bucket returns 429 RATE_LIMITED with Retry-After in seconds. Back off for at least that long; the SDKs expose it on the error.

BudgetApplies toLimit
StandardEvery public request except health20 / min
IntensiveStreams, did:webvh, credential and policy verification, MCP/A2A inspection, delegation (marked intensive below). Also counts toward Standard.2 / min
Private APIProject API keys from the consolePer plan

Under momentary load a request can also return 503 CAPACITY_EXHAUSTED; retry with exponential backoff. Cache results you reuse: verdicts include observedAt.

Reference

Errors

Every error is an RFC 9457 problem object with a stable code. Branch on code, not on title or detail. DID Resolution errors use W3C type URIs; DID.is conditions use https://did.is/problems#…. Failures on /v1/resolve include the partial trace.

Code/v1 statusW3C statusMeaning
INVALID_DID400400Syntax or method-specific identifier is invalid (incl. IP-literal did:web).
INVALID_DID_URL400400Malformed DID URL or DID parameters.
INVALID_OPTIONS400400Invalid DID resolution options, e.g. unknown parameter.
INVALID_REQUEST400—Malformed body, unknown or repeated query parameter.
NOT_FOUND404404Document, version, fragment, service or API route not found.
METHOD_NOT_ALLOWED405—HTTP method not allowed on this public route.
REPRESENTATION_NOT_SUPPORTED406406Accept header cannot be satisfied.
——410W3C binding only: the DID is deactivated (the resolution result is still returned).
PAYLOAD_TOO_LARGE413500Body over the cap (512 KiB requests, 1 MiB fetched documents).
INVALID_DID_DOCUMENT422500Document id mismatch, bad JSON, or failed did:webvh log verification.
RATE_LIMITED429429Anonymous budget exhausted. Honour Retry-After.
METHOD_NOT_SUPPORTED501501DID method not implemented.
FEATURE_NOT_SUPPORTED501501e.g. DID URL paths.
EGRESS_BLOCKED403500Target resolves to a non-public address, or scheme/userinfo refused.
UPSTREAM_UNAVAILABLE502500The identifier's host failed or timed out.
CAPACITY_EXHAUSTED503—Bounded work capacity is momentarily full; retry with backoff.
INTERNAL_ERROR500500Unexpected internal resolver error.
Problem object
HTTP/2 429
content-type: application/problem+json
retry-after: 3

{
  "type": "https://did.is/problems#RATE_LIMITED",
  "title": "Rate limited",
  "status": 429,
  "code": "RATE_LIMITED",
  "detail": "anonymous Explorer safety budget exhausted"
}
Handling · TypeScript
import { DidisClient, DidisError } from "@didis/client";

const didis = new DidisClient();
try {
  const r = await didis.resolve("did:web:identity.foundation");
  console.log(r.verdict.headline);
} catch (e) {
  if (e instanceof DidisError && e.problem.code === "RATE_LIMITED") {
    const wait = Number(e.problem.retryAfter ?? 1);
    console.warn(`Rate limited; retry after ${wait}s`);
  }
  throw e;
}
Guide

Private API & webhooks

Projects in the console add metered private resolution that is never written to public history, project API keys with one-time secrets and rotation, hourly identifier monitoring, and signed webhooks. Plans and allowances are on pricing.

  • Keys are shown once. Keep them server-side; never ship them to browsers or mobile apps.
  • Webhooks carry X-DIDIS-Event-ID and X-DIDIS-Signature: t=<createdAt>,v1=<hex HMAC-SHA256> over createdAt + "." + raw body.
  • Verify against the exact raw bytes before parsing. Retries resend the identical event, so record the event ID before side effects and acknowledge duplicates with 2xx.
  • Failed deliveries retry up to five attempts (after 1, 2, 4 and 8 minutes). Only HTTPS endpoints on port 443 and public addresses are accepted; redirects are not followed.
Verify a webhook · TypeScript
import { verifyCustomerMonitorSignature } from "@didis/client";

const secret = process.env.DIDIS_WEBHOOK_SECRET ?? "";
const tenantId = "tenant_0000000000000000000000000000000000000000000000000000000000000000";
const projectId = "project_0000000000000000000000000000000000000000000000000000000000000000";

const body = await req.text(); // raw body, before JSON parsing
const ok = await verifyCustomerMonitorSignature(
  secret, body, req.headers.get("x-didis-signature") ?? "",
  { tenantId, projectId, eventId: req.headers.get("x-didis-event-id") ?? "" },
);
if (!ok) return new Response(null, { status: 400 });

API reference · Resolution

Resolution

W3C DID Resolution (CR Draft)

GET/1.0/identifiers/{did}

DID Resolution v1 · W3C Candidate Recommendation Draft (1 Oct 2026) HTTPS binding. Accept: application/did-resolution returns the full resolution result; application/did (or */*) returns the DID document only. Errors are RFC 9457 problem objects in didResolutionMetadata.error with the specification's status codes (400 invalid DID, 404 not found, 406 representation not supported, 410 deactivated, 501 method not supported). DID URLs (fragments, ?service=, ?versionId=) are dereferenced.

ParameterInTypeDescription
did (required)pathDID or DID URLRaw or fully percent-encoded (did%3A…); decoded exactly once.
Acceptheadermedia typeapplication/did-resolution | application/did

Returns ResolutionResult or DID document

cURL
curl -s "https://did.is/api/1.0/identifiers/did%3Aweb%3Aidentity.foundation" \
  -H 'Accept: application/did-resolution'
Live against https://did.is/api; counts toward your anonymous budget.
Resolution

Resolve with evidence

GET/v1/resolve/{did}

Superset of the W3C result: verdict (plain-language headline and statements), seven independently evaluated evidence dimensions, the evidence graph, per-stage telemetry, key/linkage/TLS/history evidence and stated limitations. No scores.

ParameterInTypeDescription
did (required)pathDIDThe identifier to resolve.
noCachequerybooleanBypass and refresh the resolver cache.

Returns EnrichedResolution

cURL
curl -s "https://did.is/api/v1/resolve/did%3Aweb%3Aidentity.foundation"
Live against https://did.is/api; counts toward your anonymous budget.
Resolution · intensive

Live resolution (SSE)

GET/v1/stream/{did}

Server-sent events: one `stage` event per telemetry stage as it completes, then `result` (EnrichedResolution) or `error` (problem), then `done`.

ParameterInTypeDescription
did (required)pathDIDThe identifier to resolve.
noCachequerybooleanResolve fresh.

Returns text/event-stream

cURL
curl -sN "https://did.is/api/v1/stream/did%3Aweb%3Aidentity.foundation?noCache=true"
Resolution

Dereference a DID URL

GET/v1/dereference/{didUrl}

Selects a verification method or service by fragment, a service endpoint by ?service= (with path-traversal-safe relativeRef), or a did:webvh version by versionId / versionTime / versionNumber. Encode '#' as %23.

ParameterInTypeDescription
didUrl (required)pathDID URLe.g. did:web:example.com%23key-1

Returns DereferencingResult

cURL
curl -s "https://did.is/api/v1/dereference/did%3Aweb%3Aidentity.foundation%23key-1"
Live against https://did.is/api; counts toward your anonymous budget.
Resolution

Evidence graph

GET/v1/graph/{did}

The nodes and edges behind a verdict (subject, document, origin, TLS, keys, services, linkage) with the state of every relation. Use it to render or audit why a verdict was reached.

ParameterInTypeDescription
did (required)pathDIDThe identifier.

Returns { did, observedAt, verdict, nodes, edges }

cURL
curl -s "https://did.is/api/v1/graph/did%3Aweb%3Aidentity.foundation"
Live against https://did.is/api; counts toward your anonymous budget.

API reference · Observation

Observation

Observation history

GET/v1/history/{did}

Snapshots DID.is recorded for this identifier (coalesced when unchanged). Observations are records, not proofs of history.

ParameterInTypeDescription
did (required)pathDIDThe identifier.

Returns { did, count, history: ObservationRecord[] }

cURL
curl -s "https://did.is/api/v1/history/did%3Aweb%3Aidentity.foundation"
Live against https://did.is/api; counts toward your anonymous budget.
Observation

Semantic diff

GET/v1/diff/{did}

Compares two observations: keys added, removed and rotated (same id, new material), relationship changes, services, controller, alsoKnownAs, @context and domain-binding changes. Defaults to the latest two distinct observations.

ParameterInTypeDescription
did (required)pathDIDThe identifier.
fromquerysha256Older document hash.
toquerysha256Newer document hash.

Returns SemanticDiff

cURL
curl -s "https://did.is/api/v1/diff/did%3Aweb%3Aidentity.foundation"
Live against https://did.is/api; counts toward your anonymous budget.

API reference · Credentials

Credentials · intensive

Verify a credential

POST/v1/credentials/verify

Data Integrity (eddsa-jcs-2022, ecdsa-jcs-2019), VC-JWT 1.1 and vc+jwt. The signing key must belong to the issuer DID and be authorised for assertionMethod. Bitstring Status List entries are fetched, their list credential verified, and evaluated fail-closed. Status precedence: MALFORMED > INVALID > UNSUPPORTED > REVOKED > SUSPENDED > EXPIRED > NOT_YET_VALID > INDETERMINATE > VALID.

ParameterInTypeDescription
credential (required)bodyobject | stringCredential object, raw JSON string, or compact JWT. Raw text preserves member identity.
expectedAudiencebodystringOptional expected JWT recipient. Without it, audience is explicitly not verified.

Returns CredentialVerification

cURL
curl -s -X POST "https://did.is/api/v1/credentials/verify" \
  -H 'Content-Type: application/json' \
  -d '{"credential":{"@context":["https://www.w3.org/ns/credentials/v2"],"type":["VerifiableCredential"],"issuer":"did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK","validFrom":"2026-01-01T00:00:00Z","credentialSubject":{"id":"did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLdmQR63zLFKfvUwi3"}}}'
Credentials · intensive

Evaluate a policy

POST/v1/policies/evaluate

Evaluates declarative rules (allowedMethods, allowedCurves, minKeyBits, requireKeyRelationship, domainBinding, tlsMinDaysRemaining, verifiableHistory, maxCacheAgeSeconds, credentialStatus, credentialIssuerMustBeSubject) against observed evidence. Unevaluable rules are INDETERMINATE, never PASS.

ParameterInTypeDescription
did (required)bodystringSubject DID.
policy (required)body{ name?, rules }Rule set.
credentialbodyobject | stringOptional credential to evaluate.

Returns { evaluation, credential, resolution }

cURL
curl -s -X POST "https://did.is/api/v1/policies/evaluate" \
  -H 'Content-Type: application/json' \
  -d '{"did":"did:web:identity.foundation","policy":{"rules":{"didResolution":"required","domainBinding":"required"}}}'

API reference · Agents

Agents · intensive

Inspect an MCP server

GET/v1/mcp/inspect

Streamable HTTP: server/discover (2026-07-28) with legacy initialize fallback; paginated tools/list; SHA-256 fingerprints of every tool definition (RFC 8785); declared annotations vs heuristic side-effect classes; risk signals; drift against the previous observation. 401 responses are reported, never bypassed.

ParameterInTypeDescription
endpoint (required)queryhttps URLThe MCP endpoint.

Returns McpInspection

cURL
curl -s "https://did.is/api/v1/mcp/inspect?endpoint=https%3A%2F%2Fmcp.example.com%2Fmcp"
Agents · intensive

Inspect an A2A agent card

GET/v1/a2a/inspect

Fetches /.well-known/agent-card.json (legacy agent.json fallback), validates the A2A 1.0 shape, and verifies signatures[] (JWS over the JCS card) with keys from a DID kid or an HTTPS jku.

ParameterInTypeDescription
url (required)queryhttps URLAgent origin or card URL.

Returns A2aInspection

cURL
curl -s "https://did.is/api/v1/a2a/inspect?url=https%3A%2F%2Fagent.example"
Agents · intensive

Verify a delegation chain

POST/v1/agents/verify-delegation

DID.is Delegation Receipt v1 (experimental): compact JWS receipts, root first. Verifies signatures via capabilityDelegation keys, issuer/audience and prf hash linkage, temporal containment, capability attenuation, acyclicity and depth ≤ 8. register: true stores VALID chains for /v1/agents/verify-tool.

ParameterInTypeDescription
chain (required)bodystring[]Receipts, root first.
trustedRootsbodystring[]Acceptable root DIDs.
registerbodybooleanStore if VALID.

Returns { result: DelegationChainResult, registered }

cURL
curl -s -X POST "https://did.is/api/v1/agents/verify-delegation" \
  -H 'Content-Type: application/json' \
  -d '{"chain":[],"trustedRoots":["did:web:bank.example"]}'
Agents · intensive

Authorise a tool call

POST/v1/agents/authorize

Stateless: verifies the chain and checks that its leaf capabilities grant the tool (exact, *, ns:tool, ns:*).

ParameterInTypeDescription
chain (required)bodystring[]Receipts, root first.
tool (required)bodystringTool name.

Returns { authorization: ToolAuthorization, chain }

cURL
curl -s -X POST "https://did.is/api/v1/agents/authorize" \
  -H 'Content-Type: application/json' \
  -d '{"chain":[],"tool":"transfer"}'
Agents

Check a registered agent

GET/v1/agents/verify-tool

Answers ALLOW or DENY for an agent and tool against delegation chains previously registered with verify-delegation (register: true). Without a registered VALID chain the answer is DENY.

ParameterInTypeDescription
agent (required)queryDIDThe acting agent.
tool (required)querystringTool name.
rootqueryDIDRequire this delegation root.

Returns { agent, tool, decision, authorized, reason }

cURL
curl -s "https://did.is/api/v1/agents/verify-tool?agent=did%3Aweb%3Aagent.example&tool=transfer"
Live against https://did.is/api; counts toward your anonymous budget.

API reference · Fast verify

Fast verify

Fast DID verdict

GET/v1/fast-verify/did/{did}

Compact projection for gateways and agents: outcome, headline, dimension states, keys and document hash. Served from cache when fresh.

ParameterInTypeDescription
did (required)pathDIDThe identifier.
noCachequerybooleanBypass and refresh the resolver cache.

Returns { did, resolved, outcome, headline, statements, dimensions, keys, domainBinding, documentSha256, observedAt, cached }

cURL
curl -s "https://did.is/api/v1/fast-verify/did/did%3Aweb%3Aidentity.foundation"
Live against https://did.is/api; counts toward your anonymous budget.
Fast verify

Verify a JWS against a DID

POST/v1/fast-verify/jws

Resolves the DID in the JWS kid, requires the key in assertionMethod or authentication (or the relationship you name), and verifies the signature.

ParameterInTypeDescription
jws (required)bodystringCompact JWS with a DID URL kid.
relationshipbodystringRequired verification relationship.

Returns { status, valid, headline, signer, relationship, payload }

cURL
curl -s -X POST "https://did.is/api/v1/fast-verify/jws" \
  -H 'Content-Type: application/json' \
  -d '{"jws":"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa2hhWGdCWkR2b3REa0w1MjU3ZmFpenRpR2lDMlF0S0xHcGJubkVHdGEyZG9LI2tleS0xIn0.eyJzdWIiOiJkaWQ6a2V5Ono2TWtoYVhnQlpEdm90RGtMNTI1N2ZhaXp0aUdpQzJRdEtMR3Bibm5FR3RhMmRvSyJ9.c2lnbmF0dXJl"}'

API reference · Directory

Directory

Verified control claims

GET/v1/claims/{did}

Current, unexpired proofs that a controller demonstrated control of this DID (for example a key signature), each stating what it proves and what it does not. A claim is not legal identity.

ParameterInTypeDescription
did (required)pathDIDThe identifier.

Returns { did, claims: Claim[], listing, note }

cURL
curl -s "https://did.is/api/v1/claims/did%3Aweb%3Adid.is"
Live against https://did.is/api; counts toward your anonymous budget.
Directory

Search the directory

GET/v1/directory

Identifiers whose controller proved control and opted in to be listed. Listings cannot be bought. Unknown or repeated parameters are rejected.

ParameterInTypeDescription
qquerystringFree text, up to 100 characters.
methodquerystringDID method filter, e.g. web, key, webvh.
limitqueryinteger1–50 (default 20).
cursorquerystringnextCursor from the previous page.

Returns { entries: DirectoryEntry[], nextCursor, note }

cURL
curl -s "https://did.is/api/v1/directory?method=web&limit=5"
Live against https://did.is/api; counts toward your anonymous budget.

API reference · Service

Service

Health and capabilities

GET/health

Liveness plus the supported DID methods and implemented standard versions. Not rate limited.

Returns { status, name, version, time, supportedMethods, standards, monitoring, rateLimited }

cURL
curl -s "https://did.is/api/health"
Live against https://did.is/api; counts toward your anonymous budget.
Service

Service status

GET/v1/status

Machine-readable component status behind did.is/status: resolution, console, billing, monitoring and more.

Returns { status, checkedAt, components: Component[] }

cURL
curl -s "https://did.is/api/v1/status"
Live against https://did.is/api; counts toward your anonymous budget.