Skip to main content
Host an issuer backend so AIR can call its credential preview and issuance endpoints. You can use a hosted service or expose a local service through an HTTPS tunnel for sandbox development. A database is optional in sandbox. Revocation, issuance history, and the token status list need a database, so plan for one before production. Use any database your backend supports; the reference issuer service uses PostgreSQL. Encrypted credentials are stored in dStorage either way.

What you are hosting

Credential claims can come from an existing service or API, an existing database, or static test data. You do not need to create a new database simply to provide claims.

Fork and review the backend implementation

Fork the SD-JWT branch of the AIR issuer service. It issues SD_JWT_VC credentials under a did:web issuer DID.
  • The service runs without DATABASE_URL for sandbox testing. For issuance history, revocation, and a token status list at scale across many users, you will need to persist the relevant data in your own database.
  • Iden3 issuers use a separate branch. See Iden3 credentials.
This guide follows the issuer service used in the issuance quickstart.

Choose a hosting path

Check each provider’s current plans and idle/sleep behavior before selecting a sandbox host: Railway, Render, Koyeb, Neon, and Supabase. A sleeping service may delay the first credential request.

Prerequisites

Deploy the issuer service

The following steps use Railway as an example. Apply the same build commands, environment configuration, and public endpoint requirements on another host.
1

Create a project and select your repository

Create a Railway project and add your fork of the AIR issuer service. Use the same revision you configured and tested locally.
2

Configure build and start commands

If you enable database persistence, set the pre-deploy command to npx mikro-orm migration:up after configuring DATABASE_URL.
3

Configure the public origin and environment

Generate a public HTTPS domain in the host’s networking settings. Set ISSUER_ORIGIN to that origin without a trailing slash. Use a custom domain you control for production: the issuer DID is did:web:<host of ISSUER_ORIGIN>, so the host is your issuer identity.Add the environment values from Configure and start the issuer backend, including Partner JWT key configuration, PARTNER_JWKS, and the issuer API keys. Keep secrets in the host’s environment settings.For this sandbox example, use NODE_ENV=sandbox and sandbox API origins. Railway supplies PORT; the issuer service listens on it.
4

Optionally add a database

Skip this step if you are using a backend configuration without database persistence.The reference issuer service uses PostgreSQL. Add PostgreSQL to the project, or use an existing PostgreSQL service, and set DATABASE_URL on the issuer backend to its connection string. If you built your own backend, connect the database it uses instead.For a Railway database named Postgres, the reference variable is:
Run migrations before starting the database-backed service. This enables the persistence used by issuance history, revocation records, and the token status list.
5

Deploy and check the service

Confirm the process starts successfully, then open https://<your-host>/.well-known/did.json and https://<your-host>/.well-known/jwt-vc-issuer. The DID document’s id is the issuer DID you register with AIR. Verify that the public host routes requests to your issuer endpoints and that its TLS certificate is valid.When you use a database, also confirm connectivity and successful migrations. Use your implementation’s health checks.
Keep ISSUER_ORIGIN stable once you issue credentials. It defines the issuer DID, and revocation and status-list URLs in issued credentials refer to it.

Optional database persistence

For the reference issuer service, apply its migrations when DATABASE_URL is configured:
Without database persistence, skip migrations and database readiness checks. Do not use an empty database history as evidence that issuance failed, or rely on persistent history and revocation records being available. Connect a durable database and validate history and revocation behavior before using the issuer with real users. dStorage keeps the encrypted credential; it serves a different purpose from issuer application records. See Revoke credentials.

After hosting: connect AIR

  1. Register the issuer. Use Issuer → Settings in the sandbox or production Dashboard and enter the did:web issuer DID. Both environments are self-serve for SD-JWT issuers.
  2. Register the endpoint URLs. Configure availableVcApiUrl and issueVcApiUrl to point to POST /available-vc and POST /issue-vc on your backend, and revocationStatusApiUrl to its GET /revocation-status/:nonce route.
  3. Match the issuer API key. Register the backend’s API_KEY with AIR. AIR sends it as x-api-key; keep it out of browser code.
  4. Register the public JWKS URL. Its keys must match the Partner JWT signing key and kid. See JWKS endpoint setup.
  5. Register the web app origin. Add it under Account → Domains.
  6. Test issuance. Confirm the holder sees the preview, approves issuance, and can use the resulting credential. See the issuance quickstart.
The issuer endpoints and JWKS must be reachable by AIR over HTTPS. They may share a domain; separate public origins for every component are not required. If you enable CORS on the issuer or a reverse proxy in front of it, allow *.air3.com. Restricting origins to your own domain breaks the holder claim flow.

Go-live checklist

  • did:web document served at ${ISSUER_ORIGIN}/.well-known/did.json and its DID registered with AIR
  • Partner JWT keys (PARTNER_*) match your JWKS, and PARTNER_JWKS holds the matching public keys
  • Each Dashboard schema has a matching class in src/issuer/sd-jwt-vc-schemas/, exported from index.ts
  • generateCredentialData returns schema-valid claims, and disclosureFrame and expirySec are set per schema
  • The database is durable and its migrations are applied
  • ISSUER_ORIGIN is public HTTPS on a domain you control, and the status endpoints are reachable
  • availableVcApiUrl, issueVcApiUrl, revocationStatusApiUrl, and the API key are set in AIR
  • If you use the token status list, the partition size is final and a publish job is scheduled
  • The claim flow passes end to end with a test holder

Local development

Run the issuer locally and expose it through an HTTPS tunnel such as ngrok or cloudflared. Follow the HTTPS tunnel setup. Set ISSUER_ORIGIN and the registered issuer endpoint URLs to the tunnel address. If the tunnel address changes, update both. Add a local or managed database connection (PostgreSQL for the reference issuer service) only when your backend configuration uses database persistence.

Next steps