> ## Documentation Index
> Fetch the complete documentation index at: https://docs.baselayer.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Presenting & Verifying a Credential

How a counterparty verifies a credential presented by an agent, and what it can safely read once verification passes.

Baselayer's credentials adopt the standard signed JSON Web Tokens (JWT) format. Merchants, websites, platforms, and payment processors can verify a credential and its contents, to recognize returning customers, validate the agent represents a legitimate person or business, and that the delegation authority is current.

Each credential contains:

* A header describing how the token was signed
* A payload containing verified identity attributes
* A signature used to verify freshness and authenticity

<Info>
  ### Before you start

  The agent needs a credential minted to the counterparty's audience (see [Minting Credentials](/docs/minting-credentials)) and the private half of the agent key it was bound to. The counterparty needs nothing from Baselayer directly except the ability to resolve a `did:web` identifier.
</Info>

### The presentation flow

1. **Discover access requirements.** The agent hits the counterparty resource with no credential and is refused `401` with a `KYA-Disclosure-Scope` header naming the scope required. It fetches `GET /.well-known/kya-profile.json` to read the exact fields, purpose, legal basis, and retention for the website or transaction.
2. **Mint on demand.** Mint a credential with the required fields (see [Minting Credentials](/docs/minting-credentials)).
3. **Bind to the request.** The agent fetches a single-use nonce from the counterparty (`POST /nonces`, hosted by the counterparty, not Baselayer) and signs a Key Binding JWT over `(audience, nonce)`, appending it to the credential's wire string.
4. **Present.** `GET /resource` with the `KYA-Credential` header and the scope reference.

### How a counterparty verifies

Before reading a claim, the counterparty verifier:

* parses the SD-JWT-VC
* resolves the issuer via `did:web` (see "Resolving the issuer's DID document" below)
* verifies the issuer JWT's signature and expiry
* checks the credential's live revocation status against its referenced status list (see the [Credential Format Reference](/docs/credential-format-reference))
* hashes each disclosure and confirms it is a member of the signed digest set
* verifies the Key Binding JWT against the credential's bound key, its own audience, and the single-use nonce
* for L2, keys its returning customer record on the pairwise DID
* refuses any disclosure beyond the referenced scope's fields

A tampered disclosure fails as `DISCLOSURE_DIGEST_MISMATCH`; an over-broad presentation fails as `SCOPE_VIOLATION`. Either failure refuses the whole presentation. A counterparty never keeps the fields it is entitled to and drops the rest.

### Resolving the issuer's DID document

`GET /.well-known/did.json` — unauthenticated, cacheable for one hour. Every individual and business credential names its issuer as a `did:web` identifier; resolves it to this document and match the credential's signing key id (`kid`, from the JWT header) against `verificationMethod`. During a key rotation, both the outgoing and incoming key appear here, so a cached document doesn't reject either.

A counterparty credential is the one exception: its `sub` resolves through a separate, business-specific document. See the [Credential Format Reference](/docs/credential-format-reference).

### Implementation recommendations

* Always verify the issuer signature and the Key Binding JWT before reading anything.
* Reject credentials whose `aud` is not exactly your domain, including your own subdomains and [www](http://www). variants unless you minted for them.
* Cache the registry keys for up to an hour; refresh once on an unknown `kid`.
* Issue single-use nonces.
* Publish a `kya-profile.json` and enforce the subset rule. It is what lets you tell a regulator exactly what you can and cannot read.
* Treat revoked and expired credentials identically.

### Troubleshooting

| Problem | Fix |
| - | - |
| Verifier can't find a matching key | Re-fetch `.well-known/did.json` — don't cache past its stated TTL; a rotation may have moved the signing `kid`. |
| Presentation refused, no fields read | Check whether the failure is `DISCLOSURE_DIGEST_MISMATCH` (a value changed in transit) vs. `SCOPE_VIOLATION` (the mint requested more than the published scope) — they read the same to a naive integration but mean very different things. |
| Credential accepted but recognition doesn't persist across visits | Confirm you're keying your returning-customer record on the L2 pairwise DID (`sub`), not on the `jti` — the `jti` is unique per mint; the DID is stable per person per audience. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.