Quickstart
From a fresh clone to one credential issued and verified end-to-end, against the platform’s real REST contract — no mocks.
Prerequisites
- Docker + Docker Compose
- Java 25 (only needed if you run a service outside Docker)
./deploy.sh builds and runs everything else inside containers.
1. Start the stack
cd attest-pro
# First time only — maps *.localhost hostnames to 127.0.0.1
./deploy.sh setup
# Start everything: postgres, redis, all 9 backends, all 6 frontends, nginx
./deploy.sh start
# Confirm everything is up and see the live URL map
./deploy.sh statusFlyway migrations run automatically on each service’s first startup. Two URLs matter next:
| URL | Purpose |
|---|---|
http://localhost:9200 | API Gateway — every curl below goes through this |
http://console.localhost | Admin Console — approve the issuer you register in step 3 |
2. Register a custodial issuer
No signing key of your own needed — Attest ID generates and holds one for you (custodial signing, see System Design § Security & Key Management).
curl -s -X POST http://localhost:9200/api/v1/issuer/register \
-H "Content-Type: application/json" \
-d '{
"name": "Getting Started University",
"website_url": "https://example.edu",
"contact_email": "registrar@example.edu",
"country_code": "US"
}' | tee /tmp/issuer.jsonEvery new issuer — custodial or BYOK — starts PENDING_APPROVAL. Save issuer_did from the
response.
3. Approve it as an admin
Local dev ships a bootstrapped admin account (change these before any non-local deployment):
curl -s -X POST http://localhost:9200/api/v1/admin/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "admin@attestid.global", "password": "attest-dev-admin-2026"}' \
| tee /tmp/admin-login.json
ADMIN_TOKEN=$(jq -r .token /tmp/admin-login.json)
ISSUER_DID=$(jq -r .issuer_did /tmp/issuer.json)
curl -s -X POST "http://localhost:9200/api/v1/issuers/$ISSUER_DID/approve" \
-H "Authorization: Bearer $ADMIN_TOKEN"Or approve it by hand at http://console.localhost — this is what that approval queue does under
the hood.
4. Issue a credential
curl -s -X POST http://localhost:9200/api/v1/credentials/issue \
-H "Content-Type: application/json" \
-d "{
\"issuer_did\": \"$ISSUER_DID\",
\"holder_did\": \"did:key:z6MkExampleHolderForGettingStarted\",
\"credential_type\": \"UniversityDegreeCredential\",
\"credential_data\": { \"name\": \"Ada Lovelace\", \"degree\": \"BSc Computer Science\" }
}" | tee /tmp/issue.jsonoperation_status: SUCCESS and credential_status: ISSUED confirm it landed. Save
credential_id.
5. Verify it
CREDENTIAL_ID=$(jq -r .credential_id /tmp/issue.json)
curl -s "http://localhost:9200/api/v1/credentials/$CREDENTIAL_ID"A real verifier would instead use POST /api/v1/credentials/verify or
GET /api/v1/verify/{share_token} against a shared credential link — see
API & gRPC Reference.
What you just exercised
Next
- Sign credentials yourself instead of custodially → Onboard a BYOK issuer
- Add a new country/qualification framework → Add a jurisdiction
- Full system picture → System Design
- Something not behaving like this page says → Local Development, or
./deploy.sh logs <service>