Getting started

This mirrors the automated acceptance test (ApiFirstAcceptanceTests), so every step here is a step the platform is actually tested to support.

export DILIGENCE_BASE_URL="https://your-diligenceid-host"
export DILIGENCE_API_KEY="did_test.ak_example_0000.<secret>"

1. Get a sandbox and a key

An administrator or tenant owner provisions your sandbox once:

POST /api/sandbox
{
  "data": {
    "environment": { "environmentId": "env_...", "environmentType": "Sandbox", "status": "Active" },
    "apiClient": { "clientId": "ak_...", "environment": "Sandbox", "keyPrefix": "did_test" },
    "apiKey": "did_test.ak_....<secret>",
    "environmentCreated": true
  }
}

Copy apiKey now. It is returned exactly once and cannot be recovered — not by support, not by an administrator, not by any endpoint. Calling POST /api/sandbox again returns the same environment but mints a new key; the previous one keeps working until it is revoked.

The key is scoped to the whole developer lifecycle — organisations, issuers, credential configurations, issuance, verification policies, verification — and deliberately cannot manage API clients, the platform or tenant configuration.

Everything from here uses /v1 and that key alone.

2. Confirm the key works

curl -sS "$DILIGENCE_BASE_URL/v1/issuers" -H "Authorization: ApiKey $DILIGENCE_API_KEY"

200 means authenticated and scoped. 401 means the key is not valid. 403 means it is valid but lacks the scope for that call.

A Sandbox key sees only Sandbox resources. A Production issuer is not merely forbidden to it — it is reported as not found, because confirming that it exists would itself be a disclosure.

3. Create an issuer

curl -sS -X POST "$DILIGENCE_BASE_URL/v1/issuers" \
  -H "Authorization: ApiKey $DILIGENCE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "displayName": "Acme Workforce",
        "organisationReference": "acme-ltd",
        "issuerType": "Root",
        "configuration": {
          "canonicalIssuerUri": "https://issuer.acme.example",
          "publicMetadataUri": "https://issuer.acme.example/.well-known/openid-credential-issuer",
          "trustProfileVersion": "RootTrustProvider:Test",
          "statusMechanism": "StatusList"
        },
        "signingKey": { "algorithm": "ES256", "provider": "Software" }
      }'

Scope: issuers.manage. Returns 201 with the issuer in Draft.

The issuer is created in your key's environment — a Sandbox key cannot create a Production issuer, whatever the request body says. configuration and signingKey are optional here; omit them and use PATCH /v1/issuers/{issuerId} instead. Both routes run identical validation, because both call the same service Diligence Admin does.

Create the organisation first if you do not have one — POST /v1/organisations, scope organisations.manage.

4. Create a credential configuration

curl -sS -X POST "$DILIGENCE_BASE_URL/v1/credential-configurations" \
  -H "Authorization: ApiKey $DILIGENCE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "issuerId": "iss_example",
        "identifier": "employee-badge",
        "displayName": "Employee badge",
        "schemaId": "schema_employee_badge",
        "claims": [
          { "name": "employee_id", "displayName": "Employee ID", "dataType": "String",
            "required": true, "selectiveDisclosureAllowed": true, "displayOrder": 0 }
        ]
      }'

Scope: credential-configurations.manage. Creates the configuration, activates its first version and authorises the issuer to use it, in one call. The configuration inherits the issuer's environment, which is what stops a Sandbox configuration ever referencing a Production issuer.

dc+sd-jwt and ES256 are the platform's current format and algorithm limits, not overridable defaults.

5. Check readiness, then activate

curl -sS "$DILIGENCE_BASE_URL/v1/issuers/$ISSUER_ID/readiness" \
  -H "Authorization: ApiKey $DILIGENCE_API_KEY"

curl -sS -X POST "$DILIGENCE_BASE_URL/v1/issuers/$ISSUER_ID/activate" \
  -H "Authorization: ApiKey $DILIGENCE_API_KEY"

readyForActivation is the summary; required is the itemised checklist, each entry naming a code, whether it is satisfied and a detail. Activation runs the same checklist and returns 409 listing what is outstanding rather than activating a half-configured issuer.

6. Issue

curl -sS -X POST "$DILIGENCE_BASE_URL/v1/credentials/issuance" \
  -H "Authorization: ApiKey $DILIGENCE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
        "issuerId": "iss_example",
        "credentialConfiguration": "cfg_example",
        "externalSubjectReference": "employee-4821",
        "claims": { "subject_identifier": "employee-4821" }
      }'

201 returns a transactionId and a credentialOfferUri; the status is awaiting_wallet until the holder accepts. issuerId is required — an API key belongs to a tenant, not to one issuer.

Always send Idempotency-Key. Without it, a timeout you retry can issue two credentials.

7. Define what you want to verify

curl -sS -X POST "$DILIGENCE_BASE_URL/v1/verification-policies" \
  -H "Authorization: ApiKey $DILIGENCE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "policyIdentifier": "verified-employee",
        "displayName": "Verified employee",
        "organisationReference": "acme-ltd",
        "credentialConfigurations": ["ccr_example"]
      }'

A policy is created and activated in one call. Pass "activate": false to leave it Draft — a Draft policy cannot start a verification. suspend and reactivate are available on the same resource.

8. Verify

curl -sS -X POST "$DILIGENCE_BASE_URL/v1/verifications" \
  -H "Authorization: ApiKey $DILIGENCE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "policy": "verified-employee" }'

201 returns a verification id, a presentationUrl and a qrCodePayload. You name a policy; DiligenceID builds the OpenID4VP request, so you never construct protocol parameters.

Poll GET /v1/verifications/{id} — or better, subscribe to webhooks. status moves from pending through to verified, rejected, expired or cancelled.

Starting a verification against a suspended policy returns 400. That is the policy lifecycle working, not a malformed request.

9. Revoke

curl -sS -X POST "$DILIGENCE_BASE_URL/v1/credentials/$CREDENTIAL_ID/revoke" \
  -H "Authorization: ApiKey $DILIGENCE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Employment ended" }'

Immediate and not reversible. A later verification against a policy requiring an active status will fail.

Run it end to end

examples/quickstart/ contains the same sequence as numbered scripts:

cd developer-docs/examples/quickstart
export DILIGENCE_BASE_URL="https://your-diligenceid-host"
export DILIGENCE_API_KEY="did_test...."
export ISSUER_ID="iss_..."
./run-all.sh

Edit this page on GitHub