Skip to content

Credentials API

BaseID Cloud supports two W3C-standard credential formats. Choosing the right format depends on your privacy requirements and ecosystem.

Format value: jwt_vc_json

A W3C Verifiable Credential signed as a JSON Web Token (JWT). The entire credential — all claims, types, issuer, and subject — is encoded in the JWT payload and visible to any party that receives it.

Structure:

Header.Payload.Signature

The JWT payload contains:

{
"iss": "did:key:z6MkIssuer...",
"sub": "did:key:z6MkHolder...",
"iat": 1711459200,
"vc": {
"@context": ["https://www.w3.org/ns/credentials/v2"],
"type": ["VerifiableCredential", "CanadianDigitalID"],
"credentialSubject": {
"id": "did:key:z6MkHolder...",
"givenName": "Alice",
"familyName": "Martin",
"dateOfBirth": "1990-05-15"
}
}
}

When to use:

  • Organizational credentials (membership, employment, certificates)
  • Credentials where the verifier always needs all fields
  • Simple integrations that just need a signed JWT
  • Maximum interoperability — JWT-VC is the most widely supported format

Limitations:

  • No selective disclosure — the holder cannot hide individual claims
  • All claims are visible to every verifier that receives the credential
  • Not ideal for privacy-sensitive data (e.g., revealing date of birth when only age verification is needed)

Format value: vc+sd-jwt

A Selective Disclosure JWT Verifiable Credential. The issuer signs all claims, but each claim is individually disclosable — the holder chooses which claims to reveal to each verifier.

Structure:

IssuerJWT~Disclosure1~Disclosure2~...~

The compact serialization contains the issuer-signed JWT followed by ~-separated disclosure values. Each disclosure is a Base64-encoded JSON array [salt, claim_name, claim_value].

How selective disclosure works:

  1. Issuance: The issuer signs all claims. Each claim gets a unique salt and is hashed into the JWT’s _sd array.
  2. Presentation: The holder creates a presentation including only the disclosures for claims they want to reveal. Unrevealed claims remain as opaque hashes — the verifier can see they exist but not their values.
  3. Verification: The verifier checks the issuer’s signature on the JWT, then verifies each disclosed claim matches its hash in _sd.

Example — age verification without revealing date of birth:

The credential contains givenName, familyName, dateOfBirth, and age_over_18. The holder presents only age_over_18: true — the verifier confirms the issuer attested to this fact without learning the actual date of birth.

Always-visible claims (plain claims):

ClaimDescription
issIssuer DID
subSubject DID (if provided)
vctVerifiable Credential Type
iatIssuance timestamp

All other claims (everything in claims) become selectively disclosable.

When to use:

  • Privacy-sensitive credentials (government ID, health records, age verification)
  • GDPR/data minimization compliance — share only what’s needed
  • eIDAS 2.0 / HAIP compliance (SD-JWT VC is a required format)
  • Scenarios where different verifiers need different subsets of claims

Limitations:

  • Slightly more complex than JWT-VC for verifiers to process
  • Requires the vct (Verifiable Credential Type) field
  • Not all verifier implementations support SD-JWT yet (adoption growing rapidly)

FeatureJWT-VCSD-JWT VC
StandardW3C VC Data Model 2.0IETF SD-JWT + W3C VC
PrivacyAll claims visibleHolder chooses which claims to reveal
ComplexitySimple — standard JWTModerate — disclosures + hashing
HAIP/eIDASNot compliantCompliant (required format)
Best forOrganizational creds, certificatesGovernment ID, health, age verification
Verifier supportUniversalGrowing (all major wallets)
Data minimizationNoYes — GDPR-friendly
Credential sizeSmallerLarger (includes disclosure salts)
Do verifiers always need ALL claims?
├─ Yes → JWT-VC (simpler, universal support)
└─ No → SD-JWT VC (privacy-preserving)
Targeting eIDAS 2.0 or HAIP compliance?
├─ Yes → SD-JWT VC (required)
└─ No → Either format works
Handling personal data (name, DOB, address)?
├─ Yes → SD-JWT VC (data minimization)
└─ No → JWT-VC (simpler)

Typesubject_didWho can present
Subject-boundSet to holder’s DIDOnly the holder identified by the DID
BearerOmitted (empty)Anyone who possesses the credential

Bearer credentials are useful for transferable tokens (event tickets, vouchers). Subject-bound credentials are standard for identity documents.


POST /v1/credentials/issue
Authorization: Bearer <token>

Permission required: credentials:issue

FieldTypeRequiredDescription
formatstringYesjwt_vc_json or vc+sd-jwt
issuer_didstringYesIssuer DID (must belong to your tenant — create on DIDs page)
subject_didstringNoHolder’s DID. Omit for bearer credentials.
typesstring[]NoCredential types (default: ["VerifiableCredential"]). Add domain types like CanadianDigitalID.
claimsobjectYesJSON object with the credential’s data fields
vctstringNoVerifiable Credential Type (SD-JWT only — defaults to first non-VerifiableCredential type)
Terminal window
curl -X POST https://api.baseid.cloud/v1/credentials/issue \
-H "Authorization: Bearer bsk_live_..." \
-H "Content-Type: application/json" \
-d '{
"format": "jwt_vc_json",
"issuer_did": "did:key:z6Mk...",
"subject_did": "did:key:z6MkHolder",
"types": ["VerifiableCredential", "CanadianDigitalID"],
"claims": {
"givenName": "Alice",
"familyName": "Martin",
"dateOfBirth": "1990-05-15",
"province": "Ontario"
}
}'
Terminal window
curl -X POST https://api.baseid.cloud/v1/credentials/issue \
-H "Authorization: Bearer bsk_live_..." \
-H "Content-Type: application/json" \
-d '{
"format": "vc+sd-jwt",
"issuer_did": "did:key:z6Mk...",
"subject_did": "did:key:z6MkHolder",
"vct": "AgeVerification",
"claims": {
"age_over_18": true,
"age_over_19": true,
"age_over_21": true,
"dateOfBirth": "1990-05-15"
}
}'

The holder can later present only age_over_19: true without revealing their actual date of birth.

Example — bearer credential (event ticket)

Section titled “Example — bearer credential (event ticket)”
Terminal window
curl -X POST https://api.baseid.cloud/v1/credentials/issue \
-H "Authorization: Bearer bsk_live_..." \
-H "Content-Type: application/json" \
-d '{
"format": "jwt_vc_json",
"issuer_did": "did:key:z6Mk...",
"types": ["VerifiableCredential", "EventTicket"],
"claims": {
"event": "BaseID Conference 2026",
"venue": "Ottawa Convention Centre",
"date": "2026-06-15",
"seat": "A-42"
}
}'

No subject_did — anyone with this credential can present it.

{
"credential_id": "550e8400-...",
"format": "jwt_vc_json",
"credential": "eyJhbGciOiJFZERTQSJ9.eyJpc3Mi...",
"issuer_did": "did:key:z6Mk...",
"subject_did": "did:key:z6MkHolder"
}

The credential field contains the raw compact serialization:

  • JWT-VC: three Base64url-encoded parts separated by .
  • SD-JWT VC: JWT followed by ~-separated disclosures

GET /v1/credentials?limit=50&offset=0
Authorization: Bearer <token>

Permission required: credentials:list

ParameterTypeDefaultDescription
limitinteger50Max results (1-100)
offsetinteger0Pagination offset
[
{
"id": "550e8400-...",
"tenant_id": "6ba7b810-...",
"format": "jwt_vc_json",
"credential_data": { "raw": "eyJ...", "format": "jwt_vc_json" },
"issuer_did": "did:key:z6Mk...",
"subject_did": "did:key:z6MkHolder",
"types": ["VerifiableCredential", "CanadianDigitalID"],
"created_at": "2026-03-26T12:00:00Z",
"revoked_at": null
}
]

GET /v1/credentials/:id
Authorization: Bearer <token>

Permission required: credentials:list

Returns a single credential by ID (same shape as list items).


POST /v1/credentials/:id/revoke
Authorization: Bearer <token>

Permission required: credentials:revoke

  • Immediate: The credential’s status changes to revoked right away
  • Permanent: Revocation cannot be undone
  • Verifiable: Verifiers checking the credential will see the revoked status
  • Data preserved: The credential data is kept for audit and compliance purposes
  • No notification: The holder is not automatically notified (implement via webhooks if needed)
{
"status": "revoked",
"credential_id": "550e8400-..."
}

Returns 404 if the credential doesn’t exist or is already revoked.


Credential issuance is metered per month based on your plan:

PlanCredentials/month
Developer (free)100
Startup ($49)1,000
Business ($199)10,000
EnterpriseUnlimited

When the monthly limit is reached, issuance returns 400 with a message prompting an upgrade. Check remaining quota via the Usage API.