# Issue verifiable credentials

> Issue Verifiable Credentials (VCs) from your application and enable users to store them in their Affinidi Vault.

To integrate with the Credential Issuance service, you must:

- 
Create a schema using [Schema Builder](/products/affinidi-elements/schema-builder.md). The schema is required to create a credential offer and issue a Verifiable Credential (VC) into the user’s Affinidi Vault.

- 
Set up a [Credential Issuance Configuration](/products/affinidi-elements/credential-issuance/configuration.md) to issue a Verifiable Credential.

## Create a credential offer

After setting up the Issuance Configuration, use the [Issuance Client](/dev-tools/affinidi-tdk.md#clients) of the [Affinidi TDK](/dev-tools/affinidi-tdk.md) to enable your application to create a credential offer to Affinidi Vault users.

In this example, we will create a credential offer for a course completion use case. Follow the sample code below to initiate a credential offer to the user.

By default, the claim mode is TX_CODE, where user’s DID (holderDid) is not required.
Note on Claim Modes

If you create the credential offer with FIXED_HOLDER claim mode, the credential issuance requires the user’s DID value to be the Holder DID (holderDid). To get the user’s DID from your website, you must implement [Affinidi Login](/products/affinidi-elements/affinidi-login/how-affinidi-login-works.md) to authenticate and extract the user’s DID value from the ID Token provided.

You can explore our [Labs](/labs/integrate-affinidi-login.md) to learn how to integrate Affinidi Login into different programming languages and frameworks.

- Install the required libraries (in our example, we will use Credential Issuance service).

npm install -S @affinidi-tdk/auth-provider @affinidi-tdk/credential-issuance-client

pip install affinidi_tdk_auth_provider affinidi_tdk_credential_issuance_client

composer require affinidi-tdk/affinidi-tdk-php

<dependency>
    <groupId>com.affinidi.tdk</groupId>
    <artifactId>credential.issuance.client</artifactId>
    <version>1.4.0</version>
</dependency>

dotnet add package AffinidiTdk.CredentialIssuanceClient
dotnet add package AffinidiTdk.AuthProvider

dart pub add affinidi_tdk_auth_provider affinidi_tdk_credential_issuance_client

- Import the libraries into the code. We are importing the Credential Issuance client (Credential Issuance service) to create a credential offer and the Auth Provider to generate the Project Scoped Token for the Authorisation header.

import { IssuanceApi, Configuration, StartIssuanceInput } from '@affinidi-tdk/credential-issuance-client'
import { AuthProvider } from '@affinidi-tdk/auth-provider'

import affinidi_tdk_auth_provider
import affinidi_tdk_credential_issuance_client

require_once 'vendor/autoload.php';

use AffinidiTdk\AuthProvider\AuthProvider;
use AffinidiTdk\Clients\CredentialIssuanceClient;

import com.affinidi.tdk.authProvider.AuthProvider;
import com.affinidi.tdk.credential.issuance.client.ApiClient;
import com.affinidi.tdk.credential.issuance.client.Configuration;
import com.affinidi.tdk.credential.issuance.client.apis.IssuanceApi;
import com.affinidi.tdk.credential.issuance.client.auth.ApiKeyAuth;
import com.affinidi.tdk.credential.issuance.client.models.StartIssuanceInput;
import com.affinidi.tdk.credential.issuance.client.models.StartIssuanceInput.ClaimModeEnum;
import com.affinidi.tdk.credential.issuance.client.models.StartIssuanceInputDataInner;
import com.affinidi.tdk.credential.issuance.client.models.StartIssuanceResponse;

using AffinidiTdk.AuthProvider;
using AffinidiTdk.CredentialIssuanceClient.Api;
using AffinidiTdk.CredentialIssuanceClient.Client;
using AffinidiTdk.CredentialIssuanceClient.Model;

import 'package:dio/dio.dart';
import 'package:built_collection/built_collection.dart';
import 'package:built_value/json_object.dart';
import 'package:affinidi_tdk_auth_provider/affinidi_tdk_auth_provider.dart';
import 'package:affinidi_tdk_credential_issuance_client/affinidi_tdk_credential_issuance_client.dart';

- Generate an Authorisation token to call the client using the Personal Access Token for the specific project.

Use the Affinidi CLI [Token command](/dev-tools/affinidi-cli/manage-token.md#affinidi-token-create-token) to generate the Personal Access Token (PAT) for the Auth Provider.

If you have created the Personal Access Token (PAT) with the --key-id flag in Affinidi CLI, you must also set the keyId with the supplied value in the AuthProvider class.

// NOTE: set your variables for PAT
const privateKey = "<PAT_PRIVATE_KEY_STRING>"
const passphrase = "<PAT_KEY_PAIR_PASSPHRASE>"
const tokenId = "<PAT_ID>"
const projectId = "<PROJECT_ID>"

const authProvider = new AuthProvider({
    privateKey,
    passphrase,
    tokenId,
    projectId
})

const authConfiguration = new Configuration({
  apiKey: authProvider.fetchProjectScopedToken.bind(authProvider)
})

stats = {
  "privateKey": "<PAT_PRIVATE_KEY_STRING>",
  "passphrase": "<PAT_KEY_PAIR_PASSPHRASE>",
  "tokenId": "<PAT_ID>",
  "projectId": "<PROJECT_ID>"
}

authProvider = affinidi_tdk_auth_provider.AuthProvider(stats)

projectScopedToken = authProvider.fetch_project_scoped_token()

configuration = affinidi_tdk_credential_issuance_client.Configuration()

# Configure API key authorization: ProjectTokenAuth
configuration.api_key['ProjectTokenAuth'] = projectScopedToken

$params = [
  'privateKey' => "<PAT_PRIVATE_KEY_STRING>",
  'passphrase' => '<PAT_KEY_PAIR_PASSPHRASE>',
  'tokenId' => '<PAT_ID>',
  'projectId' => '<PROJECT_ID>'
];

$authProvider = new AuthProvider($params);

$tokenCallback = [$authProvider, 'fetchProjectScopedToken'];

Dotenv dotenv = null;
dotenv = Dotenv.configure().load();

// Alternatively you can create an auth provider by explicitly passing the configurations 
AuthProvider authProvider = new AuthProvider.Configurations()
    .projectId(dotenv.get("<PROJECT_ID>"))
    .privateKey(dotenv.get("<PAT_PRIVATE_KEY_STRING>"))
    .passphrase(dotenv.get("<PAT_KEY_PAIR_PASSPHRASE>"))
    .tokenId(dotenv.get("<PAT_ID>"))
    .build();

var authProvider = new AuthProvider(new AuthProviderParams
{
    TokenId = "YOUR_TOKEN_ID",
    PrivateKey = "YOUR_PRIVATE_KEY",
    ProjectId = "YOUR_PROJECT_ID"
    Passphrase = "YOUR_PASSPHRASE",
});

string projectScopedToken = await authProvider.FetchProjectScopedTokenAsync();

// NOTE: set your variables for PAT
final privateKey = "<PAT_PRIVATE_KEY_STRING>";
final passphrase = "<PAT_KEY_PAIR_PASSPHRASE>";
final tokenId = "<PAT_ID>";
final projectId = "<PROJECT_ID>";

final authProvider = AuthProvider(
  privateKey: privateKey,
  passphrase: passphrase,
  tokenId: tokenId,
  projectId: projectId,
);

- Initiate the Issuance module with the authorisation header and call the client method with the credential data.

credentialTypeId is the value configured on your Credential Issuance Configuration for the Supported Schemas.

async function issueCredential() {
    const api = new IssuanceApi(authConfiguration)

    const projectId = "<PROJECT_ID>"
    const request: StartIssuanceInput = {
        "data": [{
            "credentialTypeId": "UniversityDegree2024",
            "credentialData": {
                "first_name": "FirstName",
                "last_name": "LastName",
                "course": "Fundamentals of Decentralised Identity",
                "completion_date": "2024-01-01"
            }
        }],
        "claimMode": "TX_CODE"
    }

    const { data } = await api.startIssuance(projectId, request)

    return data
}

issueCredential()
    .then((data) => console.log(data))
    .catch((error) => console.log(error))

with affinidi_tdk_credential_issuance_client.ApiClient(configuration) as api_client:
    api_instance = affinidi_tdk_credential_issuance_client.IssuanceApi(api_client)

    projectId = "<PROJECT_ID>"
    request_json = {
        "data": [{
            "credentialTypeId": "UniversityDegree2024",
            "credentialData": {
                "first_name": "FirstName",
                "last_name": "LastName",
                "course": "Fundamentals of Decentralised Identity",
                "completion_date": "2024-01-01"
            }
        }],
        "claimMode": "TX_CODE"
    }

    start_issuance_input = affinidi_tdk_credential_issuance_client.StartIssuanceInput.from_dict(request_json)

    api_response = api_instance.start_issuance(projectId, start_issuance_input=start_issuance_input)

// Configure API key authorization: ProjectTokenAuth
$config = CredentialIssuanceClient\Configuration::getDefaultConfiguration()->setApiKey('authorization', '', $tokenCallback);

$apiInstance = new CredentialIssuanceClient\Api\IssuanceApi(
    new GuzzleHttp\Client(),
    $config
);

try {

    $projectId = "<PROJECT_ID>";
    $request = array(
        "data" => array(
            array(
              "credentialTypeId" => "UniversityDegree2024",
              "credentialData" => array(
                "Firstname" => "FirstName",
                "Lastname" => "LastName",
                "course" => "Fundamentals of Decentralised Identity",
                "completion_date" => "2024-01-01"
              )
            )
        ),
        "claimMode" => "TX_CODE"
    );

    $result = $apiInstance->startIssuance($projectId, $request);

    return $result;

} catch (Exception $e) {
    echo 'Exception when calling method: ', $e->getMessage(), PHP_EOL;
}

try {

    ApiClient defaultClient = Configuration.getDefaultApiClient();
    // Configure API key authorization: ProjectTokenAuth
    ApiKeyAuth ProjectTokenAuth = (ApiKeyAuth) defaultClient.getAuthentication("ProjectTokenAuth");
    ProjectTokenAuth.setApiKey(authProvider.fetchProjectScopedToken());

    // Iniialize the API client
    IssuanceApi apiInstance = new IssuanceApi(defaultClient);

    Map<String, Object> credentialData = new HashMap<String, Object>();
        credentialData.put("Firstname","Firstname");
        credentialData.put("Lastname","Lastname");
        credentialData.put("ExpiryDate","1990-01-01");
        credentialData.put("course", "Fundamentals of Decentralised Identity");
        credentialData.put("completion_date", "2024-01-01");

    // Create input for issuance service
    StartIssuanceInput startIssuanceInput = new StartIssuanceInput()
            .claimMode(ClaimModeEnum.TX_CODE)
            .data(new ArrayList<StartIssuanceInputDataInner>(
                    List.of(new StartIssuanceInputDataInner()
                            .credentialTypeId("UniversityDegree2024")
                            .credentialData(credentialData))));

    String projectId = "<PROJECT_ID>";

    // Issue the credential using the data above
    StartIssuanceResponse response = apiInstance.startIssuance(projectId, startIssuanceInput);

    return response;

} catch (Exception e) {
    e.printStackTrace();
}

Configuration config = new Configuration();

config.AddApiKey("authorization", projectScopedToken);

IssuanceApi api = new IssuanceApi(config);

StartIssuanceInputDataInner inpuData = new StartIssuanceInputDataInner(credentialTypeId: "MyPersonalInfo", credentialData: new Dictionary<string, object>()
  {
    { "Firstname", "John" },
    { "Lastname", "Doe" },
    { "ExpiryDate", "1970-01-01" },
  });

StartIssuanceInput input = new StartIssuanceInput(claimMode: StartIssuanceInput.ClaimModeEnum.TXCODE, data: [inpuData]);

var projectId = "<YOUR-PROJECT-ID>";

StartIssuanceResponse result = api.StartIssuance(projectId, input);

final issuanceClient = AffinidiTdkCredentialIssuanceClient(
  dio: Dio(BaseOptions(
    baseUrl: AffinidiTdkCredentialIssuanceClient.basePath,
    connectTimeout: const Duration(seconds: 10),
    receiveTimeout: const Duration(seconds: 10),
  )),
  authTokenHook: authProvider.fetchProjectScopedToken,
);

final issuanceApi = issuanceClient.getIssuanceApi();

final projectId = "<PROJECT_ID>";

final credentialData = {
  "first_name": "FirstName",
  "last_name": "LastName",
  "course": "Fundamentals of Decentralised Identity",
  "completion_date": "2024-01-01",
};

final credentialDataBuilder = MapBuilder<String, JsonObject>(
  credentialData.map((key, value) => MapEntry(key, JsonObject(value))),
);

final data = StartIssuanceInputDataInnerBuilder()
  ..credentialTypeId = "UniversityDegree2024"
  ..credentialData.replace(credentialDataBuilder.build());

final startIssuanceInput = StartIssuanceInputBuilder()
  ..claimMode = StartIssuanceInputClaimModeEnum.TX_CODE
  ..data = ListBuilder<StartIssuanceInputDataInner>([data.build()]);

final response = await issuanceApi.startIssuance(
  projectId: projectId,
  startIssuanceInput: startIssuanceInput.build(),
);

print(response);

The Credential Issuance service will return the credential offer URI and the Transaction Code to obtain the credential details.

Your application should send this information securely to the intended user to claim the credential offer within the configured duration in the Issuance Configuration.

```JSON
{
  "credentialOfferUri": "https://

.apse1.issuance.affinidi.io/offer/",
  "txCode": "123456",
  "issuanceId": "",
  "expiresIn": 3600
}
```

## Claiming the credential offer

To allow the recipient of the credential offer to claim the credential and store it on their Affinidi Vault, you can send the claim link via email or QR code, for example. The claim link will be in the following format with the URL encoded <CREDENTIAL_OFFER_URI> value:

```html
https://vault.affinidi.com/claim?credential_offer_uri=
```

You can use the buildClaimLink function of VaultUtils from Affinidi TDK to build a claim link.

If the credential offer was created with the Transaction Code (txCode), your application must also send the Transaction Code so the user can successfully claim the credentials.

## What’s next

  [Issue revocable credentials to your users](/products/affinidi-elements/credential-issuance/revocable-credentials.md)

  [Get notified and get a copy of the claimed credentials](/products/affinidi-elements/credential-issuance/vc-claim-notification.md)

  [Request Verifiable Credentials (VCs) from your users with their consent](/products/affinidi-elements/iota-framework.md)
