How to Use the FortID Wallet Service
A guide on using the FortID Wallet Service API to manage wallets and verifiable credentials programmatically.
This guide walks you through using the FortID Wallet Service API to manage wallets and verifiable credentials programmatically. The Wallet Service provides a backend service that handles cryptographic operations, credential storage, and protocol interactions, allowing you to build thin client applications that interact with wallets through REST API calls.
The guide covers the following scenarios:
- Creating a wallet
- Issuing a PID/SD-JWT credential to the wallet
- Verifying a claim from a PID/SD-JWT credential stored in the wallet
- Listing available verifiable credentials
Architecture
The FortID Wallet Service follows a thin client architecture where the Wallet Service acts as the backend, handling all wallet-related operations.
Thin Client Architecture Overview
In this architecture:
- Client applications make API calls to the FortID Wallet Service
- FortID Wallet Service manages:
- Cryptographic key pairs (i.e., wallet identity)
- Credential storage and retrieval
- OpenID4VCI and OpenID4VP protocol handling
- Secure credential operations (e.g., credential matching)
Key Components
- FortID Wallet Service backend - Handles all wallet operations, including key management, credential storage, and protocol interactions
- Client application - Thin layer making API calls to the FortID Wallet Service
- Integration with FortID Issuer Service - For credential issuance workflows
- Integration with FortID Verifier Service - For credential verification workflows
Benefits of This Architecture
- Centralized credential management - All credentials are stored securely in the FortID Wallet Service
- Server-side security and key management - Sensitive cryptographic material never leaves the FortID Wallet Service
- Simplified client implementation - Clients only need to make HTTP requests
- Scalable and maintainable - Centralized service can be scaled independently
Prerequisites
Before using the FortID Wallet Service, ensure you have:
- Wallet Service API access and base URL (e.g.:
https://ews.fortid.com) - (Required) Authentication (Bearer Token) -
All Wallet Service API calls must include a valid Bearer Token in the HTTP
Authorizationheader. The token is strictly bound to your wallet identity and is required for all authenticated requests (see instructions below). - (Optional) Familiarity with Issuance and Verification flows
- Access to FortID Issuer Service, for issuance scenarios (e.g.:
https://eis.fortid.com) - Access to FortID Verifier Service, for verification scenarios (e.g.:
https://evs.fortid.com) - Authentication/API key configuration for the FortID Issuer and FortID Verifier Services (e.g.:
HCLN4ZKnWYJAfyNkDnQ57gEAHuejD6MN)
NOTE: The FortID Wallet Service communicates with the issuer and verifier services via standard OID4VC flows. One could use any issuer or verifier service that supports these protocols, but we use FortID's components as they are readily available via our FortID Playground and are very easy to use.
Wallet API Authentication - Obtaining Bearer Token
A bearer token is a credential that identifies an authenticated user and allows access to designated resources.
NOTE: The bearer token is tightly linked to your test account. If you lose the token, you will lose access to your wallet and its credentials. There is no account recovery for test/playground accounts — treat your token like a password and safeguard it appropriately!
Wallet API token is retrieved by calling the https://cape.fortid.com/token endpoint.
curl -X POST "https://cape.fortid.com/token" \
-H "Content-Type: application/json"Response example:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
}This token is then used to authenticate the user for all subsequent requests.
curl -X POST "https://ews.fortid.com/{any_wallet_api_endpoint} \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"Scenario 1: Creating a Wallet
Creating a wallet generates a new wallet identity with a cryptographic key pair. The Wallet Service automatically generates a unique wallet ID and creates a public-private key pair for the wallet.
Create Wallet Request
To create a new wallet, send a POST request to the /wallets endpoint:
curl -X POST "https://ews.fortid.com/wallets" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {bearer_token_here}"Create Wallet Response
The response includes the wallet identifier and public key:
{
"walletId": "f4856bb7-d77a-4a6a-9c8a-7d7f6f639cfb",
"walletPublicKey": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEiGNz3sVg7sujG0ILMahBfr1GD/IA\nkf1RwmCY3Esk/qBr+sDWt+kz9bljRfNQEfZtZCI2DgtVQPhfvLwYo5+oFw==\n-----END PUBLIC KEY-----"
}walletIdis a unique identifier that you'll need for all subsequent operations with this walletwalletPublicKeyis the public part of the cryptographic key pair used by the wallet- The corresponding private key is securely stored by the FortID Wallet Service and never exposed
Note: The
walletIdis required for all subsequent wallet operations. Make sure to store it securely. For information on listing all wallets for an authenticated user, see Scenario 4: Listing Available Verifiable Credentials.
Scenario 2: Issuing a PID/SD-JWT Credential to the FortID Wallet
This scenario demonstrates how to issue a verifiable credential (specifically a PID in SD-JWT format) to a wallet using the Wallet Service API. This workflow integrates with the FortID Issuer Service.
Overview
The issuance flow involves two services:
- FortID Issuer Service - Creates a credential offer (see How to Issue a Verifiable Credential)
- FortID Wallet Service - Receives and stores the credential
Prerequisites
- Wallet must be created (see Scenario 1: Creating a Wallet)
- Issuer must be set up and credential issuance initiated (see How to Issue a Verifiable Credential)
- Credential Offer from the Issuer Service
Workflow
Step 1: Initiate Issuance Session
Note: Before calling the Wallet Service, you must first initiate credential issuance with the FortID Issuer Service (see How to Issue a Verifiable Credential). The
credentialOfferUrireturned in the Issuer Service's response must be used as thecredentialOfferparameter in the request below.
Start the issuance process by providing the credential offer URI from the FortID Issuer Service response:
curl -X POST "https://ews.fortid.com/wallets/{walletId}/issue/initiate" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {bearer_token_here}" \
-d '{
"credentialOffer": "openid-credential-offer://?credential_offer_uri=..."
}'Response:
{
"issuanceSessionId": "dd6473c2-2043-4523-b359-ceb596ddae66"
}The issuanceSessionId is used to track the issuance session.
Step 2: Check Issuance Status
Poll the session status to see if the issuance is complete or if user consent is required:
curl -X POST "https://ews.fortid.com/wallets/{walletId}/issue/{issuanceSessionId}/session-status" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {bearer_token_here}"Possible Statuses:
IN_PROGRESS- The issuance process is still ongoingCONSENT_REQUEST_READY- User consent is required (includesconsentRequestObject)ISSUANCE_SUCCEEDED- Credential was successfully issued and stored (includescredentialId)ISSUANCE_FAILED- Issuance failed (includesreason)
Example Response (Consent Required):
{
"status": "CONSENT_REQUEST_READY",
"consentRequestObject": {
"credentialOfferParameters": {
"credential_issuer": "https://issuer.example.com",
"credential_configuration_id": "eu.europa.ec.eudi.pid_mdoc",
"tx_code": {
"input_mode": "numeric",
"length": 6,
"description": "Enter the transaction code sent to your device"
}
}
}
}Example Response (Success):
{
"status": "ISSUANCE_SUCCEEDED",
"credentialId": "3c833a84-94eb-477d-91ca-a9f68c5cda31"
}Step 3: Provide Consent (if required)
If the status check returns CONSENT_REQUEST_READY, provide consent to continue:
curl -X POST "https://ews.fortid.com/wallets/{walletId}/issue/{issuanceSessionId}/consent" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {bearer_token_here}" \
-d '{
"txCode": "123456"
}'Note: The
txCodeparameter is optional and only required if the issuer has enabled transaction codes in the pre-authorized code flow.
Notes
- The Wallet Service handles the OpenID4VCI protocol flow automatically
- Credentials are automatically stored in the wallet upon successful issuance
- Supported credential formats include SD-JWT (
vc+sd-jwt) and MSO-MDOC (mso_mdoc) - The issuance session may expire after a configured timeout
Scenario 3: Verifying a Claim from a PID/SD-JWT Credential
This scenario demonstrates how to use the Wallet Service to present credentials for verification. This workflow integrates with the FortID Verifier Service.
Overview
The verification flow involves:
- Verifier Service - Initiates verification request (see How to Verify a Verifiable Credential)
- Wallet Service - Presents credential from the wallet
Prerequisites
- Wallet must contain at least one verifiable credential (from Scenario 2: Issuing a PID/SD-JWT Credential to the FortID Wallet)
- Verifier must initiate a verification session (see How to Verify a Verifiable Credential)
authorization_requestfrom Verifier Service
Workflow
Step 1: Initiate Verification Session
Note: Before calling the Wallet Service, you must first initiate verification with the FortID Verifier Service (see How to Verify a Verifiable Credential). The
authorization_requestreturned in the Verifier Service's response must be used as theauthorizationRequestparameter in the request below.
Start the verification process by providing the authorization request from the FortID Verifier Service response:
curl -X POST "https://ews.fortid.com/wallets/{walletId}/verify/initiate" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {bearer_token_here}"
-d '{
"authorizationRequest": "openid4vp://authorize?client_id=..."
}'Response:
{
"verificationSessionId": "550e8400-e29b-41d4-a716-446655440000"
}The verificationSessionId is used to track the verification session.
Step 2: Check Verification Status
Check the status to see if credentials match and if user consent is required:
curl -X POST "https://ews.fortid.com/wallets/{walletId}/verify/{verificationSessionId}/session-status" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {bearer_token_here}"Possible Statuses:
IN_PROGRESS- The verification process is still ongoingCONSENT_REQUEST_READY- User consent is required (includesconsentRequestObjectwith matched credentials)VERIFY_SUCCEEDED- Credential verification completed successfully (includesresponse)VERIFY_FAILED- Credential verification failed (includesreason)
Example Response (Consent Required):
{
"status": "CONSENT_REQUEST_READY",
"consentRequestObject": {
"validated_authorization_request": {
"client_id": "https://verifier.example.com",
"response_uri": "https://verifier.example.com/protocol/oid4vp/authorization-response",
"presentation_definition": {
"input_descriptors": [
{
"constraints": {
"fields": [
{
"path": ["$.given_name"]
},
{
"path": ["$.family_name"]
}
]
}
}
]
}
},
"matching_credentials": [
{
"id": "3c833a84-94eb-477d-91ca-a9f68c5cda31",
"decoded_payload": {
"vc+sd-jwt": {
"given_name": "John",
"family_name": "Doe"
}
}
}
]
}
}The matching_credentials array contains all credentials in the wallet that satisfy the verification request.
Each credential includes its id (to be used in Step 3) and decoded_payload (for user preview).
Step 3: Present Credential
Once you've selected which credential to present, submit it:
curl -X POST "https://ews.fortid.com/wallets/{walletId}/verify/{verificationSessionId}/present" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {bearer_token_here}" \
-d '{
"selectedCredentialId": "3c833a84-94eb-477d-91ca-a9f68c5cda31"
}'Response:
On success, returns HTTP 200.
Example Response (Success):
{
"status": "VERIFY_SUCCEEDED",
"response": {
"vp_token": "...",
"presentation_submission": {
"id": "32f54163-aaaa-48f1-93d8-ff217bdb0653",
"definition_id": "32f54163-aaaa-48f1-93d8-ff217bdb0653",
"descriptor_map": [
{
"id": "68a3214f-bbbb-6895-dada-22ff7bdb3434",
"path": "$",
"format": "vc+sd-jwt"
}
]
}
}
}Notes
- The FortID Wallet Service handles the OpenID4VP protocol flow automatically
- The wallet automatically matches credentials based on verifier requirements
- Selective disclosure is supported - only requested claims are presented
- Supported credential formats include SD-JWT (
vc+sd-jwt) and MSO-MDOC (mso_mdoc) - The verification session may expire after a configured timeout
Scenario 4: Listing Available Verifiable Credentials
This scenario demonstrates how to retrieve all credentials stored in a FortID Wallet's wallet instance, or a specific credential by ID.
Overview
You can query credentials stored in a wallet for various purposes:
- Inspecting wallet contents
- Finding specific credentials
- Verifying credential storage after issuance
- Debugging and auditing
List All Credentials
Retrieve all credentials stored in a wallet:
curl -X GET "https://ews.fortid.com/wallets/{walletId}/credentials" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {bearer_token_here}"Response:
[
{
"credentialId": "24fbd94e-472c-4258-b712-8da0a4a6e787",
"issuerUrl": "https://issuer.example.com/",
"claims": {
"cnf": {
"jwk": {
"x": "YQQVjIs0juPPqIVAWh2f9oetDAkygZV2YmEpOSOIb08",
"y": "VdHNNNY0gyHynAiL8q28QKTik_g777mtNjLYQ37ngVQ",
"alg": "ES256",
"crv": "P-256",
"kid": "91c26923-9925-4ea8-8007-06763be38716",
"kty": "EC",
"use": "sig"
}
},
"iat": 1765896143,
"iss": "https://issuer.example.com/",
"vct": "urn:eu.europa.ec.eudi:pid:1",
"birthdate": "2005-12-21",
"given_name": "John",
"family_name": "Smith",
"nationalities": [
"GB"
],
"date_of_expiry": "2029-12-15T14:35:13+00:00",
"place_of_birth": "GB",
"issuing_country": "GB",
"date_of_issuance": "2025-11-16T14:35:13+00:00",
"issuing_authority": "GB"
},
"format": "vc+sd-jwt",
"type": "urn:eu.europa.ec.eudi:pid:1",
"credentialConfigurationId": "eu.europa.ec.eudi.pid_vc_sd_jwt",
"validity": {
"createdAt": "2025-11-16T14:42:23Z",
"receivedAt": "2025-11-16T14:42:23Z"
}
}
]Note: Similarly, one can use the FortID Wallet Service API to fetch a specific credential by its
credentialId.
Credential Object Structure
Each credential object includes:
credentialId- Unique identifier for the credentialissuerUrl- The URL of the Issuertype- Type of credential (e.g.,eu.europa.ec.eudi.pid.1,org.iso.18013.5.1.mDL)format- Format of the credential (e.g.,vc+sd-jwt,mso_mdoc)credentialConfigurationId- Credential configuration ID found in the issuer metadataclaims- Full credential data (all claims in plain text). The structure depends on the credential format:- For
vc+sd-jwtformat: Claims are directly in the object (no namespace wrapper) - For
mso_mdocformat: Claims are nested with the namespace type as the key (e.g.,{"eu.europa.ec.eudi.pid.1": {...}})
- For
validity- The validity information of the credential:validFrom- Timestamp before which the credential is not yet validvalidUntil- Timestamp after which the credential is no longer validcreatedAt- Timestamp at which the signature was createdreceivedAt- Timestamp at which the credential was received by the wallet
Note: Returned credentials show full data. This API shows all the data under user's control. Unlike selective disclosure where a user shares only a part of their data to the interested (i.e., verifier) party.
Additional Operations
List All Wallets
Retrieve all wallets for the authenticated user:
curl -X GET "https://ews.fortid.com/wallets" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {bearer_token_here}"Response:
[
{
"walletId": "f4856bb7-d77a-4a6a-9c8a-7d7f6f639cfb",
"walletPublicKey": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
},
{
"walletId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"walletPublicKey": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
}
]Delete Wallet
Permanently remove a wallet and all its credentials:
curl -X DELETE "https://ews.fortid.com/wallets/{walletId}" \
-H "Authorization: Bearer {bearer_token_here}"Response:
{
"walletId": "f4856bb7-d77a-4a6a-9c8a-7d7f6f639cfb",
"walletPublicKey": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
}Warning: Deleting a wallet permanently removes the wallet and all its credentials. This action cannot be undone.
Complete Workflow Example
This example demonstrates a complete end-to-end workflow:
- Create a wallet
- Issue a PID credential to the wallet
- List credentials to verify storage
- Present credential for verification
- Check verification result
# Step 1: Create wallet
WALLET_RESPONSE=$(curl -X POST "https://ews.fortid.com/wallets" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {bearer_token_here}"
)
WALLET_ID=$(echo $WALLET_RESPONSE | jq -r '.walletId')
echo "Created wallet: $WALLET_ID"
# Step 2: Issue credential (using credentialOffer from Issuer Service)
# First, get credentialOffer from Issuer Service (see How to Issue a Verifiable Credential guide)
ISSUANCE_RESPONSE=$(curl -X POST "https://ews.fortid.com/wallets/$WALLET_ID/issue/initiate" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {bearer_token_here}" \
-d '{
"credentialOffer": <PASTE_THE_CREDENTIAL_OFFER_HERE>
}')
ISSUANCE_SESSION_ID=$(echo $ISSUANCE_RESPONSE | jq -r '.issuanceSessionId')
# Check issuance status
curl -X POST "https://ews.fortid.com/wallets/$WALLET_ID/issue/$ISSUANCE_SESSION_ID/session-status" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {bearer_token_here}"
# Step 3: List credentials
curl -X GET "https://ews.fortid.com/wallets/$WALLET_ID/credentials" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {bearer_token_here}"
# Step 4: Verify credential (using authorizationRequest from Verifier Service)
# First, get authorizationRequest from Verifier Service (see How to Verify a Verifiable Credential guide)
VERIFY_RESPONSE=$(curl -X POST "https://ews.fortid.com/wallets/$WALLET_ID/verify/initiate" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {bearer_token_here}" \
-d '{
"authorizationRequest": <PASTE_THE_AUTHORIZATION_REQUEST_HERE>
}')
VERIFY_SESSION_ID=$(echo $VERIFY_RESPONSE | jq -r '.verificationSessionId')
# Check verification status and get matched credentials
VERIFY_STATUS=$(curl -X POST "https://ews.fortid.com/wallets/$WALLET_ID/verify/$VERIFY_SESSION_ID/session-status" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {bearer_token_here}"
)
CREDENTIAL_ID=$(echo $VERIFY_STATUS | jq -r '.consentRequestObject.matching_credentials[0].id')
# Present credential
curl -X POST "https://ews.fortid.com/wallets/$WALLET_ID/verify/$VERIFY_SESSION_ID/present" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {bearer_token_here}" \
-d "{
\"selectedCredentialId\": \"$CREDENTIAL_ID\"
}"
# Step 5: Check verification result
curl -X POST "https://ews.fortid.com/wallets/$WALLET_ID/verify/$VERIFY_SESSION_ID/session-status" \
-H "Content-Type: application/json"Troubleshooting
Wallet Not Found Errors
- Error:
404 - Wallet with ID ... not found - Solution: Verify that the
walletIdis correct and the wallet exists. Use the list wallets endpoint to see all available wallets.
Credential Issuance Failures
- Error:
ISSUANCE_FAILEDstatus - Possible Causes:
- Invalid credential offer URI
- Wallet not found
- Protocol compatibility issues
- Issuer service unavailable
- Solution: Check the
reasonfield in the status response for detailed error information.
Verification Session Timeouts
- Error:
404 - Verify session with ID ... not found - Solution: Verification sessions expire after a configured timeout. Initiate a new verification session if the previous one has expired.
Protocol Compatibility Issues
- Error:
501 - Unsupported functionality - Possible Causes:
- Unsupported credential format
- Unsupported protocol feature
- Solution: Ensure you're using supported credential formats (
vc+sd-jwtormso_mdoc) and protocol features.
Additional Resources
- Wallet Service API Reference - Full API documentation
- How to Issue a Verifiable Credential - Issuer Service guide
- How to Verify a Verifiable Credential - Verifier Service guide
- OpenID for Verifiable Credential Issuance (OpenID4VCI) - Specification
- OpenID for Verifiable Presentations (OpenID4VP) - Specification