🛠️
Technical Documentation
  • Introduction
    • About Empeiria
  • Empe Blockchain
    • Overview
    • Chain Architecture
      • Auth
      • Authz
      • Bank
      • Distribution
      • Governance
      • Staking
      • IBC
      • DidDoc
      • Vesting
      • Minter
      • Stablefee
      • LinkedResources
  • EVDI
    • EVDI Architecture
    • Self-Sovereign Identity
      • Technical Foundations
      • Roles in the SSI framework
      • Protocols and Standards
  • User Guide
    • Empe DID Wallet
      • Intro
      • Download and first launch
      • Create or import did
      • Main screen overview
      • How to claim credential from issuer
      • How to use credential with verifier
      • Settings and other options
    • Keplr Wallet and Blockchain Operations
      • How to Connect Keplr Wallet
    • Ping Pub operation
    • Staking Tokens Guide
    • Voting on Governance Proposals Guide
    • Sending Tokens Guide
  • Developer Guide
    • Tutorial: Credential Issuance & Verification
      • Overview
      • Understanding Key Concepts
      • Project Setup
      • Deploying the Issuer
      • Uploading the Credential Schema
      • Issuing Credentials
      • Frontend for Credential Issuance
      • Testing Credential Issuance
      • Deploying the Verifier
      • Setting Up the Verification Flow
      • Creating a Verification Endpoint
      • Creating a Protected Dashboard
      • Testing the Verification Flow
      • Summary & Next Steps
    • One-click deployment
      • Introduction
      • Registration
      • Login
      • Creating an Issuer
      • Issuer Data Description
      • Creating a Verifier
      • Verifier Data Description
    • Issuer
      • Terminology and Concepts
      • DID Document Management
      • Schema Management
      • Issuing Credentials and Interacting With Wallets
      • Revocations
      • Security Considerations
      • Error Handling and Troubleshooting
      • Future Enhancements
      • References and Standards
      • FAQ
    • Verifier
      • Terminology and Concepts
      • Architecture Overview
      • Core Responsibilities
      • Query Language
      • Client Configuration
      • Frontend Integration
      • Revocations
      • Server-Side VP Queries
      • Security Considerations
      • Error Handling and Troubleshooting
      • Future Enhancements
      • References and Standards
      • FAQ
    • Wallet SDK (Coming soon)
    • Introduction to cosmwasm
  • Validator Guide
    • Important links
    • Validators Guide
      • New validator
      • Hardware requirements
      • Required software installation
      • Go installation
      • Install prebuild binary
      • Install binary from source code (option B)
      • Configure a node
      • Cosmovisor setup
      • Install wasmvm
      • Sync with state-sync
      • Full state sync from archive snapshot
      • Latest snapshot
      • Run a Validator
      • Migration to v0.2.2
      • Migration to v0.3.0
      • Migration to v0.4.0
    • FAQ
  • Airdrop
    • On-Chain Testnet Airdrop
    • Faucet Guide: How to Claim Testnet Tokens?
  • Appendix
    • Glossary
Powered by GitBook
On this page
  • 1. Create Offering
  • Request Body
  • Examples
  • Response
  • 2. QR Code Generation
  • 3. Wallet Scans & Authorization
  • 3.1. Retrieve Offering Details
  • 3.2. Authorization Code Flow (Targeted Offering Only)
  • 4. Token Exchange
  • 5. Credential Retrieval
  • 6. Credential Storage & Presentation
  1. Developer Guide
  2. Issuer

Issuing Credentials and Interacting With Wallets

Issuing credentials involves creating an offering and enabling recipients to claim them via their wallets. Wallets, in turn, authenticate, exchange tokens, and retrieve Verifiable Credentials, storing them locally for later presentation.


1. Create Offering

POST /api/v1/offerings
x-client-secret: [Client Secret]
Content-Type: application/json

Request Body

  • credential_type (string, required): The type of credential to be issued.Example: "ProofOfPurchase"

  • credential_subject (object, required): A JSON object containing subject data.Example:

    {
      "ticket": "ticket123",
      "seat": "A12",
      "description": "Concert ticket"
    }
  • recipient (string, optional): The DID of the recipient. If provided, this will create a Targeted Offering and require proof of DID ownership. If omitted, this will create an Open Offering (any wallet scanning the QR code can claim it).Example: "did:empe:testnet:123456789abcdef"

  • redemption_limit (integer, optional): Maximum number of times this offering may be redeemed. Must be ≥ 1.Example: 1

  • expires_at (string, optional): ISO 8601 timestamp (in UTC) after which this offering cannot be redeemed.Example: "2025-05-09T13:14:55.000Z"


Examples

Targeted Offering

{
  "credential_type": "ProofOfPurchase",
  "credential_subject": {
    "ticket": "ticket123",
    "seat": "A12",
    "description": "Concert ticket"
  },
  "recipient": "did:empe:testnet:123456789abcdef",
  "redemption_limit": 1,
  "expires_at": "2025-05-09T13:14:55.000Z"
}

Open Offering

{
  "credential_type": "ProofOfAttendance",
  "credential_subject": {
    "event": "Blockchain Conference 2025",
    "date": "2025-06-10"
  }
}

Response

On success, the API responds with HTTP 200 and a JSON body containing:

  1. All fields of the newly created Offering entity, and

  2. Three additional URLs/identifiers to facilitate QR‐code rendering and direct link access:

{
  "id": "offering-abc123",
  "credential_issuer": "did:empe:issuer:987654321abcdef",
  "credential_configuration_ids": [
    "config1-id",
    "config2-id"
  ],
  "display": {
    "name": "Summer Concert Pass",
    "locale": "en-US"
  },
  "grants": {
    "authorization_code": {
      "issuer_state": "auth-state-xyz"
    }
  },
  "credential_subject": {
    "ticket": "ticket123",
    "seat": "A12",
    "description": "Concert ticket"
  },
  "recipient": "did:empe:testnet:123456789abcdef",     // or null if open offering
  "redemption_limit": 1,                                  // or null if no limit
  "expires_at": "2025-05-09T13:14:55.000Z",               // or null if no expiration
  "redemptions": [],                                      // array of any existing redemptions (empty on creation)
  "qr_code_url": "https://<HOST_ADDRESS>/offerings/offering-abc123/qr-code",
  "offering_id": "offering-abc123",
  "offering_full_url": "https://<HOST_ADDRESS>/offerings/offering-abc123"
}

Field Descriptions

  • id (string): The UUID of the newly created offering (matches offering_id).

  • credential_issuer (string): The DID of the issuer that will sign/issue the credential.

  • credential_configuration_ids (array of strings): List of one or more configuration IDs that determine how the credential is generated (e.g., template IDs).

  • display (object):

    • name (string): Human‐readable name for this offering (e.g., "Summer Concert Pass").

    • locale (string): Locale code used for display purposes (e.g., "en-US").

  • grants (object): Authorization details; typically contains an authorization_code object with an issuer_state field for the OIDC4VCI flow.

  • credential_subject (object): The same JSON object provided in the request, representing any payload data that the credential will carry.

  • recipient (string or null): If this was a Targeted Offering, the recipient DID. If omitted at creation, comes back as null.

  • redemption_limit (integer or null): If a limit was specified, that integer. Otherwise null.

  • expires_at (string or null): ISO 8601 UTC timestamp when this offering expires. If no expiration was set, this is null.

  • redemptions (array): List of redemption records (empty immediately after creation). Each element has the shape of the OfferingRedemption entity (not shown here).

  • qr_code_url (string): A URL to fetch a dynamically generated QR code. Any client (web/mobile) should render that image so that end‐users can scan it with a compatible wallet.

  • offering_id (string): Repeat of the id field (useful for clarity in client code).

  • offering_full_url (string): A direct link to the public‐facing offering page (can embed a widget or landing page to start the claim/issuance flow).


Notes:

  • The CreateOfferingRequestDto validations ensure that credential_type is a non‐empty string, that credential_subject is a non‐empty object, and that optional fields (like recipient, redemption_limit, and expires_at) adhere to their respective formats.

  • If you pass recipient, the resulting offering will require the wallet to prove ownership of that DID before claiming. If you omit recipient, any wallet scanning the QR code (at qr_code_url) can claim the credential.

  • Clients should render the QR code at qr_code_url (for example, embedding it in an <img> tag or displaying it in a mobile app).

  • offering_full_url can be used to embed into email campaigns or landing pages so that users can click directly instead of scanning a code.

  • The redemptions array will remain empty until someone claims/redeems the offering; thereafter, each redemption record will be populated under that array.

2. QR Code Generation

  • The client application retrieves qr_code_url and generates a corresponding QR code.

  • End users scan the QR code with their SSI-compatible wallet.


3. Wallet Scans & Authorization

3.1. Retrieve Offering Details

When a wallet follows the QR code, it calls:

GET /api/v1/offerings/{offering_id}
  • If this is an Open Offering, the wallet proceeds directly to token exchange.

  • If this is a Targeted Offering, the wallet must prove DID ownership via Verifiable Presentation.


3.2. Authorization Code Flow (Targeted Offering Only)

  1. Create Authorization Request

POST /api/v1/authorizations
Content-Type: application/json

Request Body

  • presentation_definition (object, required): The Presentation Definition that specifies which credentials the holder must present. Must conform to the Presentation Definition schema.

  • state (string, required): Random string (32–64 characters, alphanumeric, hyphens, underscores) to prevent CSRF attacks.

  • nonce (string, required): Random string (32–64 characters, alphanumeric, hyphens, underscores) to ensure request uniqueness and prevent replay attacks.

Example Request:

{
  "presentation_definition": {
    "id": "proof-of-purchase-definition",
    "input_descriptors": [
      {
        "id": "purchase_credential",
        "schema": [
          {
            "uri": "https://did.eg/schema/ProofOfPurchase.json"
          }
        ],
        "constraints": {
          "fields": [
            {
              "path": ["$.credentialSubject.ticket"],
              "filter": {
                "type": "string",
                "minLength": 1
              }
            }
          ]
        }
      }
    ]
  },
  "state": "abcdef1234567890abcdef1234567890abcdef12",
  "nonce": "1234567890abcdef1234567890abcdef12345678"
}

Response Body On success, HTTP 200 with a JSON payload matching the AuthorizationRequestResponseDto:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "presentation_definition": {
    "id": "proof-of-purchase-definition",
    "input_descriptors": [
      {
        "id": "purchase_credential",
        "schema": [
          {
            "uri": "https://did.eg/schema/ProofOfPurchase.json"
          }
        ],
        "constraints": {
          "fields": [
            {
              "path": ["$.credentialSubject.ticket"],
              "filter": {
                "type": "string",
                "minLength": 1
              }
            }
          ]
        }
      }
    ]
  },
  "state": "abcdef1234567890abcdef1234567890abcdef12",
  "nonce": "1234567890abcdef1234567890abcdef12345678",
  "expires_at": "2025-06-12T14:30:00Z",
  "code": null,
  "holder_did": null,
  "used": false
}
  • id (string, UUID): Unique identifier for the newly created authorization request.

  • presentation_definition (object): Echoes the Presentation Definition supplied, indicating exactly which credentials must be presented.

  • state (string, 32–64 chars): The CSRF‐protection string provided by the client. Must be stored on the client side and matched during validation.

  • nonce (string, 32–64 chars): The uniqueness string provided by the client. Should be embedded in the Verifiable Presentation to prevent replay.

  • expires_at (string, ISO 8601 date‐time): Timestamp after which this authorization request is no longer valid (e.g., "2025-06-12T14:30:00Z").

  • code (string or null): The authorization code that will be returned once the Verifiable Presentation has been successfully validated. Initially null.

  • holder_did (string or null): The holder’s DID once they have presented valid credentials. Initially null.

  • used (boolean): Indicates whether the authorization request has already been used to issue a code. Initially false.


  1. Submit Verifiable Presentation & Validate

POST /api/v1/authorizations/{authorization_id}/validate
Content-Type: application/json

Replace {authorization_id} with the UUID returned from step 1.

Request Body

  • presentation_submission (object, required): Maps the holder’s credentials to the presentation_definition from step 1. Must conform to the Presentation Submission schema.

  • vp_token (object, required): The Verifiable Presentation (VP) itself—either a JWT or JSON-LD object—that contains one or more credentials.

Example Request:

{
  "presentation_submission": {
    "definition_id": "proof-of-purchase-definition",
    "descriptor_map": [
      {
        "id": "purchase_credential",
        "format": "jwt_vc",
        "path": "$.verifiableCredential[0]"
      }
    ]
  },
  "vp_token": {
    "@context": [
      "https://www.w3.org/2018/credentials/v1",
      "https://www.w3.org/2018/credentials/examples/v1"
    ],
    "type": ["VerifiablePresentation"],
    "verifiableCredential": [
      {
        "@context": ["https://www.w3.org/2018/credentials/v1"],
        "type": ["VerifiableCredential", "ProofOfPurchase"],
        "issuer": "did:empe:issuer:987654321abcdef",
        "issuanceDate": "2025-05-01T12:00:00Z",
        "credentialSubject": {
          "id": "did:empe:123456789",
          "ticket": "ticket123",
          "seat": "A12",
          "description": "Concert ticket"
        },
        "proof": {
          "type": "JwtProof2020",
          "jwt": "eyJhbGciOiJF..."
        }
      }
    ],
    "proof": {
      "type": "JwtProof2020",
      "jwt": "eyJhbGciOiJF..."
    }
  }
}

Response Body On successful validation, HTTP 200 with:

{
  "code": "validAuthorizationCode"
}
  • code (string): The one-time authorization code that the holder will exchange for a credential or access token in the next step of the OIDC4VCI flow.

If validation fails (e.g., missing required credential, invalid presentation structure, or nonce/state mismatch), the server will respond with an appropriate 4xx error and an explanatory message.


4. Token Exchange

POST /api/v1/tokens
Content-Type: application/json

Request Body

  • authorization_code (string, required): The one-time code obtained from the previous authorization step.

    • Must be alphanumeric (letters, digits), hyphens or underscores only.

    • Length between 32 and 128 characters.

    • Cannot be empty.

Example Request:

{
  "authorization_code": "AF3DiOZy_x1jF9mKJqUwN5JDnpxk0GlwJMdfQOiAbc"
}

Response Body On success, HTTP 200 with:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6ZW1wZToweDEyMzQ1Njc4OTBhYmNkZWYiLCJpYXQiOjE2NjQ4OTc3NDIsImV4cCI6MTY2NDkwMTM0MiwiaXNzIjoiaHR0cHM6Ly9pc3N1ZXItc2VydmljZS5leGFtcGxlLmNvbSIsImF1ZCI6Imh0dHBzOi8vaXNzdWVyLXNlcnZpY2UuZXhhbXBsZS5jb20ifQ.Kt-SbnKU9S7BbCvHVsgCU3KnUYvekrFQCRRjYSlbGC0",
  "token_type": "Bearer",
  "expires_in": 3600
}
  • access_token (string): A JWT that the client (wallet) uses to authenticate subsequent API calls.

  • token_type (string): Always set to "Bearer".

  • expires_in (number): The lifetime of the access token in seconds (e.g., 3600 means it expires in 1 hour).


5. Credential Retrieval

Using the access_token obtained in step 4, the wallet retrieves the issued Verifiable Credential by its credential_id:

POST /api/v1/credentials/{credential_id}
Authorization: Bearer JWTAccessToken

Replace {credential_id} with the UUID of the issued credential you want to fetch.


Example Response

{
  "id": "credential-xyz789",
  "type": "ProofOfPurchase",
  "issuer": "did:empe:issuer123",
  "recipient": "did:empe:123456789",
  "credentialSubject": {
    "ticket": "ticket123",
    "seat": "A12",
    "description": "Concert ticket"
  },
  "vc": {
    "@context": [
      "https://www.w3.org/2018/credentials/v1",
      "https://www.w3.org/2018/credentials/examples/v1"
    ],
    "id": "urn:uuid:credential-xyz789",
    "type": ["VerifiableCredential", "ProofOfPurchase"],
    "issuer": "did:empe:issuer123",
    "issuanceDate": "2025-05-01T12:00:00Z",
    "credentialSubject": {
      "id": "did:empe:123456789",
      "ticket": "ticket123",
      "seat": "A12",
      "description": "Concert ticket"
    },
    "proof": {
      "type": "JwtProof2020",
      "jwt": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9..."
    }
  }
}
  • id (string): The unique identifier (UUID) of the issued credential.

  • type (string): The type of credential that was minted (e.g., "ProofOfPurchase").

  • issuer (string): The DID of the issuer that signed the Verifiable Credential.

  • recipient (string): The DID of the credential recipient (holder).

  • credentialSubject (object): The same subject data that was passed when creating the offering—contains fields like ticket, seat, description, etc.

  • vc (object): The complete Verifiable Credential JSON-LD object, including:

    • @context (array of URIs)

    • id (URN or same as id field)

    • type (array, e.g., ["VerifiableCredential", "ProofOfPurchase"])

    • issuer (DID)

    • issuanceDate (ISO 8601 timestamp)

    • credentialSubject (nested object with holder DID and subject fields)

    • proof (cryptographic proof, e.g., a JWT or JSON-LD proof)



6. Credential Storage & Presentation

  • Once received, the wallet stores the Verifiable Credential locally.

  • The user can later present this credential to verifiers as needed.


By following this combined flow—using the endpoints exactly as specified—issuers can support both Targeted and Open offerings. Wallets authenticate, exchange tokens, retrieve credentials, and store them locally, ensuring a secure, user-friendly SSI experience.

PreviousSchema ManagementNextRevocations

Last updated 4 days ago