Single Issuance

View as Markdown
When to use

You are issuing one credential to one subject — the most common case for ad-hoc certifications, manual KYC, etc.

Before you begin
  • Issuer role.
  • Subject DID (the holder shares it with you).
  • A schema you can issue against.
Steps
  1. Navigate to /issuer/credentials/issue. 2. Pick the Schema (and version) from the dropdown.
  2. Pick your Issuer DID (auto-populated if you have only one).
  3. Enter the Subject DID.
  4. Fill the Claims form — the form is auto-generated from the schema’s properties.
  5. (Optional) Set Validity period:
    • validFrom — defaults to now.
    • validUntil — optional explicit expiry.
  6. Toggle Anchor credential hash on chain if needed.
  7. Click Issue. The portal calls POST /api/v1/credentials/issue (vc.go:96).
API & SDK
cURLTypeScript SDK
$curl -X POST https://adid.dev/api/v1/credentials/issue \
> -H "Authorization: Bearer $ACCESS_TOKEN" \
> -H "Content-Type: application/json" \
> -d '{
> "issuerDid":"did:adi:0xOrg...",
> "subjectDid":"did:adi:0x9a2c...",
> "schemaId":"schema-uuid-1",
> "claims":{
> "membershipId":"mem-2026-0001",
> "tier":"gold",
> "validUntil":"2027-04-26T00:00:00Z"
> },
> "anchorOnChain": true
> }'
1const vc = await client.issueCredential({
2 issuerDid: 'did:adi:0xOrg...',
3 subjectDid: 'did:adi:0x9a2c...',
4 schemaId: 'schema-uuid-1',
5 type: ['VerifiableCredential', 'OrganisationMembership'],
6 credentialSubject: {
7 membershipId: 'mem-2026-0001',
8 tier: 'gold',
9 validUntil: '2027-04-26T00:00:00Z',
10 },
11 anchorOnChain: true,
12});
13console.log(vc.id, vc.proof.proofValue);

Response:

1{
2 "credential": {
3 "@context":["https://www.w3.org/ns/credentials/v2"],
4 "id":"urn:uuid:...",
5 "type":["VerifiableCredential","OrganisationMembership"],
6 "issuer":"did:adi:0xOrg...",
7 "credentialSubject":{ "id":"did:adi:0x9a2c...", "membershipId":"mem-2026-0001", "tier":"gold", "validUntil":"2027-04-26T00:00:00Z" },
8 "credentialStatus":{ "type":"StatusList2021Entry", "statusListIndex":"42", "statusListCredential":"..." },
9 "proof":{ "type":"Ed25519Signature2020", "proofValue":"z58D..." }
10 },
11 "txHash":"0xabcd..."
12}
Verify

The issued VC appears in /issuer/credentials (your issuance log) and the holder sees it in their /credentials list.

Troubleshooting
CodeCauseFix
403 ROLE_REQUIREDRole not issuerOnboard (§4.1).
400 SCHEMA_VALIDATION_FAILEDClaims don’t match schemaCheck the schema’s required fields and types.
404 SUBJECT_DID_NOT_FOUNDSubject DID not resolvableVerify the DID.
400 ISSUER_DID_DEACTIVATEDYour issuer DID is deactivatedUse a different issuer DID.

4.3.2. Bulk Issuance from CSV ##### When to use

You have hundreds-to-millions of subjects and uniform claims (one row per subject). The bulk pipeline parses the CSV, validates each row, signs in batches, and (optionally) anchors per-batch on chain.

Before you begin
  • A CSV file with header row matching the schema’s properties.
  • One column for subjectDid (or subjectEmail if your platform deployment is configured to look-up DID from email — see ops config).
Steps
  1. Navigate to /issuer/credentials/bulk. 2. Pick the Schema.
  2. Drag-drop the CSV file (max 50 MB).
  3. The portal parses the headers and shows a Column mapping preview — drag schema fields onto CSV columns if needed.
  4. (Optional) Toggle Anchor batch hashes.
  5. Click Start issuance.

The pipeline:

StageWhat happensFailure handling
ParseCSV → rowsReject upload on header mismatch.
ValidatePer-row JSON Schema checkFailed rows recorded; pipeline continues.
QueueIssuance jobs queued for the worker
Sign + InsertWorker signs VC + inserts rowFailed rows recorded.
Anchor (optional)Batch hash → SchemaRegistry / RevocationRegistryRetry on RPC failure.
ReportFinal report with success/failed counts; downloadable failure CSV.
Monitoring

The portal renders a live progress bar (SSE-driven) showing n / total processed, m failed.

API

Bulk uploads use a multi-part endpoint:

$curl -X POST https://adid.dev/api/v1/credentials/bulk-issue \
> -H "Authorization: Bearer $ACCESS_TOKEN" \
> -F "schemaId=schema-uuid-1" \
> -F "issuerDid=did:adi:0xOrg..." \
> -F "anchorBatches=true" \
> -F "csv=@membership-2026.csv"
Verify

The job appears in /issuer/credentials/bulk?jobId=.... Once complete, download the success CSV (with credential IDs) and the failure CSV (with reasons per row).

Troubleshooting
SymptomCauseFix
Header mismatch on uploadCSV columns don’t map to schemaAdjust the column mapping or fix the CSV.
All rows failedSchema is wrong, or issuer DID lacks permissionSpot-check one row with single issuance.
Job stalledWorker queue back-pressureWait; the worker auto-resumes.

4.3.3. Anchoring Credential Hashes ##### Why anchor?

Anchoring writes a keccak256(serializedVC) to the SchemaRegistry (or to a dedicated credentials index, depending on contract config). Verifiers can then prove a credential existed at a given time without the platform’s involvement.

ℹ️ Info — Anchoring credential hashes is opt-in per credential. For low-value credentials, skip it (saves gas). For high-stakes credentials (regulated KYC, real-estate ownership), always anchor.

How
  • For single issuance: toggle Anchor on chain in the form.
  • For bulk: toggle Anchor batch hashes (anchors one tx per batch of N rows; check ops for the current batch size).
  • Programmatically: anchorOnChain: true on the issuance request.
Verify

Re-fetch the credential. The metadata.anchored: true flag and metadata.txHash should be populated.