Skip to Content
Get StartedQuickstart

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 status

Flyway migrations run automatically on each service’s first startup. Two URLs matter next:

URLPurpose
http://localhost:9200API Gateway — every curl below goes through this
http://console.localhostAdmin 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.json

Every 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.json

operation_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