Install Node.js & CredCLI
Step 1 — Install Node.js (>= 18)
CredCLI requires Node.js 18 or later. Download it from nodejs.org or use a version manager:
Using nvm (macOS / Linux)
nvm install 20
nvm use 20
node --version
Using winget (Windows)
winget install OpenJS.NodeJS.LTS
node --version
Step 2 — Install CredCLI
Install globally for persistent use, or run one-off with npx:
# Option A: Install globally (recommended)
npm install -g @credcli/cli
# Option B: Run without installing
npx @credcli/cli <command>
Step 3 — Verify installation
credcli help
You should see a list of commands. If not, ensure npm bin -g is on your PATH.
Cowork VM Setup
Cowork VMs have two constraints that require a one-time workaround:
- The default npm global prefix is not writable — install into
~/.npm-globalinstead. - The mounted workspace folder blocks file deletion (EPERM) — a one-line patch silences this after render.
Step 1 — Install into a writable prefix
npm install -g @credcli/cli --prefix ~/.npm-global
export PATH="$HOME/.npm-global/bin:$PATH"
credcli --version
Step 2 — Run the Cowork setup script
This installs Playwright's Chromium browser into the package directory (so it survives session resets) and applies the EPERM patch to cli.mjs. Safe to re-run — each step checks before acting.
bash ~/.npm-global/lib/node_modules/@credcli/cli/scripts/setup.sh
Step 3 — Use the browser path when rendering
Every credcli run command needs the PLAYWRIGHT_BROWSERS_PATH variable pointing at the package-local browser:
PLAYWRIGHT_BROWSERS_PATH=~/.npm-global/lib/node_modules/@credcli/cli/.browsers \
credcli run <job> --format png
The setup script prints this exact command at the end so you can copy it from there.
Claim Your Chainletter Token
Every CredCLI operation requires a Chainletter workspace token. You receive this as a shortlink URL from your Chainletter account.
# Claim your token from a Chainletter shortlink
credcli register https://yourserver.chainletter.io/jwt/abc123
# Verify it was saved correctly
credcli register -i
This creates a token.json file in your working directory containing:
{
"jwt": "eyJhbGci...", // Bearer token for API calls
"webhookUrl": "https://yourserver.chainletter.io/webhook/...",
"tenant": "yourname", // Used as .data/ subdirectory
"groupname": "spring-2026",
"expires": "2027-01-01T00:00:00Z"
}
401 Unauthorized during send or stamp, your token has expired. Run credcli register <new-url> with a fresh shortlink from Chainletter.Set workspace identity
Your issuer name and logo are automatically injected into every rendered credential as {{WorkspaceIssuer}} and {{WorkspaceLogo}}. Set them once:
credcli workspace --issuer "Acme University"
credcli workspace --logo ./logo.png # PNG/JPG/SVG → embedded as base64
credcli workspace # view current values
Expand Claude Skills
CredCLI is designed to be orchestrated by Claude Code. Run the commands below in your working directory — Claude Code picks up the reference files automatically and gains detailed knowledge of every command, flag, and workflow:
# Write CLAUDE.md (short quick-reference, auto-loaded by Claude Code)
credcli help -c
# Write SKILL.MD (full agent reference with API docs and patterns)
credcli help -s
CLAUDE.md in your working directory. Once present, Claude understands every CredCLI command and the full workflow, and can run steps on your behalf.Stamped Credential Workflow
The full end-to-end workflow takes a CSV of recipients, renders one credential per row, uploads to IPFS, and blockchain-stamps the collection. Each step is idempotent — safe to repeat or resume.
Set workspace identity
Your issuer name and logo stamp every credential automatically. Do this once per workspace.
credcli workspace --issuer "Acme University"
credcli workspace --logo ./logo.png
Discover available templates
List all templates with their dimensions and required CSV columns. Always run this before creating a job.
credcli templates
Create a job
A job is a folder that holds the template, CSV, and all output files. Use --template N with the number from credcli templates.
credcli new --template 4 # template 4 = Diploma
# Creates: .data/{tenant}/jobs/job001/
Load recipients CSV
Your CSV header must exactly match the template's field names (case-sensitive). Run credcli templates to get the exact column list. The new job also creates an empty mailmerge.csv with the correct header — use it as a starter.
credcli csv job001 students.csv
Example CSV rows:
FullName,Title,Institution,Major,IssueDate,CredentialID
Jane Smith,Bachelor of Science,Acme University,Computer Science,2026-06-01,CRED-0001
Alex Jones,Bachelor of Arts,Acme University,History,2026-06-01,CRED-0002
Render credentials
Headless Chromium renders one PDF or PNG per CSV row. Use --resume to skip rows already rendered.
credcli run job001 --format pdf
# or: --format png (default)
# or: --limit 5 (render first 5 rows only, for testing)
# or: --resume (skip already-rendered files)
credcli output job001 # list generated file paths
Assign a Chainletter collection
Link this job to a named collection on Chainletter. Use --network public to make credentials publicly accessible on IPFS (required for claim links in emails).
credcli assign job001 spring-2026 --network public
Upload to Chainletter (IPFS)
Uploads all output files to Chainletter. Files already uploaded are deduplicated automatically (409 = safe to ignore). Use --yes to skip confirmation.
credcli send job001 --yes
Blockchain stamp
Stamps the collection with an immutable blockchain postmark. This is permanent and irreversible. After stamping, each file gets a claim link and a verification link.
credcli stamp job001
credcli output job001 before stamping.Generate claim emails
Creates one .eml per recipient with their credential claim link and QR code embedded. Also produces an all_recipients.mbox archive and a mail_merge_manifest.csv.
credcli email job001
# To use a specific email template:
credcli email job001 --email-template credential-claim-email_600x900.html
Emails are saved to output/mail_merge/. If SMTP is configured in workspace settings, they are sent automatically.
Full workflow at a glance
# ── One-shot copy-paste workflow ─────────────────────────────────────────────
credcli register https://server.chainletter.io/jwt/abc123
credcli workspace --issuer "Acme University" --logo ./logo.png
credcli templates
credcli new --template 4
credcli csv job001 students.csv
credcli run job001 --format pdf
credcli assign job001 spring-2026 --network public
credcli send job001 --yes
credcli stamp job001
credcli email job001
Check remaining credits
credcli credits
Mail Merge
OptionalMail merge is the core of CredCLI's batch processing: one CSV row = one credential = one email. You don't have to use the email step, but it completes the delivery loop from stamped credential to recipient inbox.
How it works
After credcli stamp job001, each credential has a unique claim URL stored in job.json. The email command uses your CSV data plus those claim URLs to render an email template for each recipient:
credcli email job001
# Output: job001/output/mail_merge/
# ├── Jane_Smith_CRED-0001.eml
# ├── Alex_Jones_CRED-0002.eml
# ├── all_recipients.mbox ← archive of all emails
# └── mail_merge_manifest.csv ← metadata: name, email, file, claim URL
CSV format rules
- First row is the header — column names are case-sensitive and must match the template's field list exactly
- One row per credential; blank rows are skipped
{{FieldName}}placeholders in the HTML template are replaced by the matching column value- Extra columns in the CSV are ignored; missing columns render as empty string
- Include an
Emailcolumn if you want email delivery
All available field names
These are the recognized placeholder names for use in templates and CSV headers:
Custom email templates
The email template is itself an HTML file with {{Placeholder}} syntax. The built-in template (template #6) works out of the box. To use a custom one:
credcli email job001 --email-template my-email-template.html
SMTP auto-send
If SMTP credentials are stored in workspace settings (.data/{tenant}/workspace.json), emails are sent automatically rather than written to .eml files. Configure SMTP via the web UI (credcli serve) or set environment variables:
[email protected]
TEST_SENDER_PW=your-app-password
Public vs. private collections
--network public before stamping. You cannot change this after stamping.Web UI Alternative
Every CLI command has a browser-based equivalent. The web UI is useful for visual template editing, drag-and-drop CSV upload, and monitoring render progress.
credcli serve # opens localhost:3037
credcli serve --port 8080 # custom port
Template Editor
Live split-view HTML editor with placeholder chips and instant preview rendering.
CSV Upload
Drag-and-drop zone with field validation and recipient preview table.
Render Progress
Real-time progress log and output file grid with PDF/PNG thumbnails.
Chainletter Panel
Collection assignment, upload, stamp, and email generation in one place.
Built-in Template Reference
Run credcli templates to see the full field list for each template at any time.
| # | Name | Size | Key CSV Fields |
|---|---|---|---|
| 1 | Achievement Badge | 600×600 |
FullName, CourseName, Institution, IssueDate, CredentialID, BadgeLevel |
| 2 | Certificate of Achievement | 1200×900 |
FullName, Title, CourseName, IssueDate, CredentialID |
| 3 | Course Completion Certificate | 1200×900 |
FullName, CourseName, IssueDate, CredentialID, Hours, GPA |
| 4 | Diploma | 1200×900 |
FullName, Title, Institution, Major, IssueDate, CredentialID |
| 5 | Transcript | 1200×1600 |
FullName, Institution, GPA, Major, IssueDate + 12 course rows |
| 6 | Credential Claim Email | 600×900 |
FullName, Email, Title, VerificationURL type: email |
Custom templates
Any HTML file in .data/{tenant}/templates/ with a metadata comment is recognized as a template:
<!-- Add this near the top of your HTML file -->
<!--CREDCLI:{"name":"My Diploma","width":1200,"height":900,
"fields":["FullName","Programme","IssueDate","CredentialID"]}-->
<!-- Use {{FieldName}} placeholders in the body -->
<h1>{{FullName}}</h1>
<p>{{Programme}} — {{IssueDate}}</p>
After saving, run credcli templates — your template appears immediately. Use credcli serve for a live browser-based editor.
Error Reference
| Symptom | Cause | Fix |
|---|---|---|
| No token registered | token.json missing or wrong directory |
Run credcli register <url> in the project root |
| Invalid template number | --template N out of range |
Run credcli templates to see the valid range |
| Credentials render blank | CSV column names don't match template fields | Check credcli templates for exact field names (case-sensitive) |
| 409 Conflict on send | File already uploaded (deduplication) | Safe to ignore — the skipped count is reported |
| 401 Unauthorized on send/stamp | JWT token expired | Run credcli register <url> with a fresh shortlink |
| No claim links after stamp | Collection was assigned as private | Re-create the job and use --network public before stamping |
| Chromium not found | Playwright browsers not installed | Run npx playwright install chromium |
Full command reference
| Command | Purpose | Key flags |
|---|---|---|
register <url> | Claim Chainletter token | -i show token info |
workspace | Show / set issuer name + logo | --issuer --logo |
templates | List templates + their fields | — |
new | Create a credential job | --template N |
list | List jobs + recipient counts | — |
csv <job> <file> | Load recipient CSV into a job | — |
run <job> | Render credentials to PDF/PNG | --format --limit --resume |
preview <job> | Render row 1 and open the file | --row N --format |
output <job> | Print absolute paths of output files | — |
assign <job> <id> | Link job to Chainletter collection | --network public|private |
send <job> | Upload files to Chainletter | -y auto-confirm |
stamp <job> | Blockchain-stamp the collection | — |
email <job> | Generate .eml + mbox for recipients | --email-template <file> |
credits | Show remaining stamp credits | — |
delete <job> | Delete a job folder | -y skip confirm |
serve | Start web UI at localhost:3037 | --port N |
help | Show usage | -c write CLAUDE.md, -s write SKILL.md |