> ## 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.

# Credential Format Reference

A technical companion to [Minting Credentials](/docs/minting-credentials) and [Presenting & Verifying a Credential](/docs/presenting-and-verifying-a-credential) for implementers who need to parse or verify the wire format directly rather than through the shipped SDK.

### Credential format

Baselayer's KYA credential is a `~`-joined SD-JWT-VC:

```
<Issuer-signed JWT> ~ <Disclosure 1> ~ <Disclosure 2> ~ … ~ <Disclosure n> ~
```

The issuer-signed JWT (`typ: vc+sd-jwt`, `alg: EdDSA`) carries the always-disclosed `verification` block in cleartext, the agent-key binding (`cnf`), and a `_sd` array of digests: one per selectively disclosable attribute. The plaintext values are never in this part:

```json theme={null}
{
  "iss": "did:web:registry.baselayer.com",
  "vct": "kya:Individual",
  "sub": "did:web:registry.baselayer.com:dids:merchant.example.com:519fdc0c604eef0d4ced23ffff2e0ddd",
  "aud": "merchant.example.com",
  "cnf": { "jwk": { "kty": "OKP", "crv": "Ed25519", "x": "11qYAYKx...Ro" } },
  "status": { "status_list": { "idx": 85, "uri": "https://registry.baselayer.com/statuslists/1" } },
  "_sd_alg": "sha-256",
  "verification": { "verification_level": 3, "active_keys": ["phone_otp", "dob_verified"] },
  "_sd": ["P3eJ9QO_-1k1ieR25P3XwrUZHERyNcj9ZeLYWM6jMOA", "..."]
}
```

Each Disclosure is `base64url(JSON [salt, claim_path, claim_value])` — the only place the plaintext appears. The trailing empty segment is where the agent appends its Key Binding JWT at present time.

`status.status_list` is the credential's live revocation check — an OAuth Token Status List (IETF `draft-ietf-oauth-status-list`) bit; a verifier fetches the list at `uri` and confirms this credential's `idx` isn't flagged revoked, independent of the expiry and digest checks below. This is what lets a credential be killed before it naturally expires.

### How a verifier checks for tampering

For each Disclosure, compute `base64url(sha-256(<wire segment exactly as received>))` and confirm it's present in the signed `_sd` array. Only after that membership check passes is the claim read. Because the digest covers the exact encoded bytes, altering a single value changes the digest, drops it out of `_sd`, and the presentation is rejected as `DISCLOSURE_DIGEST_MISMATCH`. The issuer never re-signs — integrity rides entirely on the one signed digest set.

Omitting a Disclosure leaves its digest harmlessly in `_sd` (a digest alone reveals nothing) with the value simply absent — this is what lets one mint serve a narrower use case, and what lets a verifier enforce `SCOPE_VIOLATION` against any Disclosure outside a published scope.

### Business credentials

A business credential carries **two** independently signed scopes end to end: `<business JWT> ~ <business Disclosures> ~ <actor JWT> ~ <actor Disclosures> ~`. Each scope's `verification` / `active_keys` is always-disclosed; `business.*` and `actor.user.*` leaves are the salted Disclosures.

### Counterparty credentials

One plain EdDSA JWS (`typ: bl-counterparty+jwt`), no Disclosures, no Key Binding JWT. Every claim is public-record fact and the credential is served verbatim at a well-known path rather than presented per-request.

Its `sub` is **not** the domain-based pairwise DID individual/business credentials use — it is keyed on the business's public registry coordinates: `did:web:<registry-domain>:businesses:<jurisdiction>:<filing-number>`, resolved via `GET /businesses/{jurisdiction}/{filing_number}/did.json` rather than the issuer's main `.well-known/did.json`. The separate `domain` claim is what a verifier actually matches against the site it's visiting.


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