Repository: TBD54566975/ssi-service
Pull request #118 (2022-10-01 01:56:12)

SIP3: Support Signing of Credentials
- Supports signing credentials with known keys, stored in key storage.
- Manifest and dependent APIs, tests, have been updated accordingly.
- Split out existing server tests to their own files for clarity

Separate verification APIs will come in a follow-up PR.

codecov-commenter: # [Codecov](https://codecov.io/gh/TBD54566975/ssi-service/pull/118?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) Report
> Merging [#118](https://codecov.io/gh/TBD54566975/ssi-service/pull/118?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) (c8d1bc8) into [main](https://codecov.io/gh/TBD54566975/ssi-service/commit/bacfbeba7ec15252d6281b9060a05915b2b8f98b?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) (bacfbeb) will **increase** coverage by `2.60%`.
> The diff coverage is `18.42%`.

```diff
@@            Coverage Diff             @@
##             main     #118      +/-   ##
==========================================
+ Coverage   27.89%   30.49%   +2.60%     
==========================================
  Files          18       18              
  Lines        1219     1246      +27     
==========================================
+ Hits          340      380      +40     
+ Misses        831      809      -22     
- Partials       48       57       +9     
```


| [Impacted Files](https://codecov.io/gh/TBD54566975/ssi-service/pull/118?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) | Coverage Δ | |
|---|---|---|
| [pkg/server/router/credential.go](https://codecov.io/gh/TBD54566975/ssi-service/pull/118/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975#diff-cGtnL3NlcnZlci9yb3V0ZXIvY3JlZGVudGlhbC5nbw==) | `6.89% <0.00%> (-0.06%)` | :arrow_down: |
| [pkg/server/router/keystore.go](https://codecov.io/gh/TBD54566975/ssi-service/pull/118/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975#diff-cGtnL3NlcnZlci9yb3V0ZXIva2V5c3RvcmUuZ28=) | `12.69% <0.00%> (-0.87%)` | :arrow_down: |
| [pkg/server/router/manifest.go](https://codecov.io/gh/TBD54566975/ssi-service/pull/118/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975#diff-cGtnL3NlcnZlci9yb3V0ZXIvbWFuaWZlc3QuZ28=) | `4.12% <0.00%> (-0.18%)` | :arrow_down: |
| [pkg/service/keystore/keystore.go](https://codecov.io/gh/TBD54566975/ssi-service/pull/118/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975#diff-cGtnL3NlcnZpY2Uva2V5c3RvcmUva2V5c3RvcmUuZ28=) | `43.07% <43.75%> (+29.28%)` | :arrow_up: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975)


Patch:
diff --git a/doc/swagger.yaml b/doc/swagger.yaml
index 112a1a9..259c607 100644
--- a/doc/swagger.yaml
+++ b/doc/swagger.yaml
@@ -1,4 +1,14 @@
 definitions:
+  credential.CredentialContainer:
+    properties:
+      credential:
+        $ref: '#/definitions/credential.VerifiableCredential'
+      credentialJWT:
+        type: string
+      id:
+        description: Credential ID
+        type: string
+    type: object
   credential.CredentialSchema:
     properties:
       id:
@@ -472,6 +482,8 @@ definitions:
     properties:
       credential:
         $ref: '#/definitions/credential.VerifiableCredential'
+      credentialJwt:
+        type: string
     type: object
   github.com_tbd54566975_ssi-service_pkg_server_router.CreateDIDByMethodRequest:
     properties:
@@ -537,6 +549,8 @@ definitions:
     properties:
       credential:
         $ref: '#/definitions/credential.VerifiableCredential'
+      credentialJwt:
+        type: string
       id:
         type: string
     type: object
@@ -544,7 +558,7 @@ definitions:
     properties:
       credentials:
         items:
-          $ref: '#/definitions/credential.VerifiableCredential'
+          $ref: '#/definitions/credential.CredentialContainer'
         type: array
     type: object
   github.com_tbd54566975_ssi-service_pkg_server_router.GetDIDByMethodResponse:
@@ -657,24 +671,25 @@ definitions:
     type: object
   github.com_tbd54566975_ssi-service_pkg_server_router.SubmitApplicationRequest:
     properties:
-      application:
-        $ref: '#/definitions/manifest.CredentialApplication'
-      requesterDid:
+      applicantDid:
         description: Once we have JWT signed wrapper that can get the did this can
           be removed
         type: string
+      application:
+        $ref: '#/definitions/manifest.CredentialApplication'
     required:
+    - applicantDid
     - application
-    - requesterDid
     type: object
   github.com_tbd54566975_ssi-service_pkg_server_router.SubmitApplicationResponse:
     properties:
-      credentials:
-        items:
-          $ref: '#/definitions/credential.VerifiableCredential'
-        type: array
-      response:
+      credential_response:
         $ref: '#/definitions/manifest.CredentialResponse'
+      verifiableCredential:
+        description: this is an interface type to union Data Integrity and JWT style
+          VCs
+        items: {}
+        type: array
     type: object
   manifest.CredentialApplication:
     properties:
@@ -815,6 +830,8 @@ definitions:
     properties:
       credential:
         $ref: '#/definitions/credential.VerifiableCredential'
+      credentialJwt:
+        type: string
     type: object
   pkg_server_router.CreateDIDByMethodRequest:
     properties:
@@ -880,6 +897,8 @@ definitions:
     properties:
       credential:
         $ref: '#/definitions/credential.VerifiableCredential'
+      credentialJwt:
+        type: string
       id:
         type: string
     type: object
@@ -887,7 +906,7 @@ definitions:
     properties:
       credentials:
         items:
-          $ref: '#/definitions/credential.VerifiableCredential'
+          $ref: '#/definitions/credential.CredentialContainer'
         type: array
     type: object
   pkg_server_router.GetDIDByMethodResponse:
@@ -1000,24 +1019,25 @@ definitions:
     type: object
   pkg_server_router.SubmitApplicationRequest:
     properties:
-      application:
-        $ref: '#/definitions/manifest.CredentialApplication'
-      requesterDid:
+      applicantDid:
         description: Once we have JWT signed wrapper that can get the did this can
           be removed
         type: string
+      application:
+        $ref: '#/definitions/manifest.CredentialApplication'
     required:
+    - applicantDid
     - application
-    - requesterDid
     type: object
   pkg_server_router.SubmitApplicationResponse:
     properties:
-      credentials:
-        items:
-          $ref: '#/definitions/credential.VerifiableCredential'
-        type: array
-      response:
+      credential_response:
         $ref: '#/definitions/manifest.CredentialResponse'
+      verifiableCredential:
+        description: this is an interface type to union Data Integrity and JWT style
+          VCs
+        items: {}
+        type: array
     type: object
   rendering.ColorResource:
     properties:
@@ -1800,7 +1820,7 @@ paths:
         "200":
           description: OK
           schema:
-            $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetSchemasResponse'
+            $ref: '#/definitions/pkg_server_router.GetSchemasResponse'
         "500":
           description: Internal server error
           schema:
@@ -1818,14 +1838,14 @@ paths:
         name: request
         required: true
         schema:
-          $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateSchemaRequest'
+          $ref: '#/definitions/pkg_server_router.CreateSchemaRequest'
       produces:
       - application/json
       responses:
         "201":
           description: Created
           schema:
-            $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateSchemaResponse'
+            $ref: '#/definitions/pkg_server_router.CreateSchemaResponse'
         "400":
           description: Bad request
           schema:
@@ -1854,7 +1874,7 @@ paths:
         "200":
           description: OK
           schema:
-            $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetSchemaResponse'
+            $ref: '#/definitions/pkg_server_router.GetSchemaResponse'
         "400":
           description: Bad request
           schema:
diff --git a/go.mod b/go.mod
index f0197d8..bc899e1 100644
--- a/go.mod
+++ b/go.mod
@@ -4,7 +4,7 @@ go 1.19
 
 require (
 	github.com/BurntSushi/toml v1.2.0
-	github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20220921215642-f749c4d89b8b
+	github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20221001002612-ec8641015c21
 	github.com/ardanlabs/conf v1.5.0
 	github.com/dimfeld/httptreemux/v5 v5.4.0
 	github.com/go-playground/locales v0.14.0
diff --git a/go.sum b/go.sum
index 6467abe..36431af 100644
--- a/go.sum
+++ b/go.sum
@@ -6,6 +6,8 @@ github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20220920154030-cbf6b4665a6d h1:NW/
 github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20220920154030-cbf6b4665a6d/go.mod h1:jkbaZT2qU+g6weOHpjPdxuHdmfOApdT8CQg41GEyZhA=
 github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20220921215642-f749c4d89b8b h1:0IJJPN8qBGEINcNheyMN/VztIj/Wu9OLzoaEdwfKMWM=
 github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20220921215642-f749c4d89b8b/go.mod h1:jkbaZT2qU+g6weOHpjPdxuHdmfOApdT8CQg41GEyZhA=
+github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20221001002612-ec8641015c21 h1:tsvfXXVW2rqdPIYJ5uLSUhTXG4C6niB9FmjpgQE/f9w=
+github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20221001002612-ec8641015c21/go.mod h1:jkbaZT2qU+g6weOHpjPdxuHdmfOApdT8CQg41GEyZhA=
 github.com/ardanlabs/conf v1.5.0 h1:5TwP6Wu9Xi07eLFEpiCUF3oQXh9UzHMDVnD3u/I5d5c=
 github.com/ardanlabs/conf v1.5.0/go.mod h1:ILsMo9dMqYzCxDjDXTiwMI0IgxOJd0MOiucbQY2wlJw=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
diff --git a/internal/credential/model.go b/internal/credential/model.go
new file mode 100644
index 0000000..99fcccf
--- /dev/null
+++ b/internal/credential/model.go
@@ -0,0 +1,40 @@
+package credential
+
+import (
+	"github.com/TBD54566975/ssi-sdk/credential"
+	"github.com/TBD54566975/ssi-sdk/credential/signing"
+	"github.com/pkg/errors"
+)
+
+// CredentialContainer acts as an abstraction over both possible credential representations
+// JWT representations are parsed upon container creation, while the original JWT is maintained
+type CredentialContainer struct {
+	// Credential ID
+	ID            string
+	Credential    *credential.VerifiableCredential
+	CredentialJWT *string
+}
+
+func NewCredentialContainerFromJWT(credentialJWT string) (*CredentialContainer, error) {
+	cred, err := signing.ParseVerifiableCredentialFromJWT(credentialJWT)
+	if err != nil {
+		return nil, errors.Wrap(err, "could not parse credential from JWT")
+	}
+	return &CredentialContainer{
+		ID:            cred.ID,
+		Credential:    cred,
+		CredentialJWT: &credentialJWT,
+	}, nil
+}
+
+func (cc CredentialContainer) IsValid() bool {
+	return cc.ID != "" && (cc.HasDataIntegrityCredential() || cc.HasJWTCredential())
+}
+
+func (cc CredentialContainer) HasDataIntegrityCredential() bool {
+	return cc.Credential != nil && cc.Credential.Proof != nil
+}
+
+func (cc CredentialContainer) HasJWTCredential() bool {
+	return cc.CredentialJWT != nil
+}
diff --git a/magefile.go b/magefile.go
index 77fefd6..e29159a 100644
--- a/magefile.go
+++ b/magefile.go
@@ -1,5 +1,4 @@
 //go:build mage
-// +build mage
 
 package main
 
diff --git a/pkg/server/router/credential.go b/pkg/server/router/credential.go
index ccccc45..824459e 100644
--- a/pkg/server/router/credential.go
+++ b/pkg/server/router/credential.go
@@ -9,6 +9,7 @@ import (
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 
+	credmodel "github.com/tbd54566975/ssi-service/internal/credential"
 	"github.com/tbd54566975/ssi-service/internal/util"
 	"github.com/tbd54566975/ssi-service/pkg/server/framework"
 	"github.com/tbd54566975/ssi-service/pkg/service/credential"
@@ -62,7 +63,8 @@ func (c CreateCredentialRequest) ToServiceRequest() credential.CreateCredentialR
 }
 
 type CreateCredentialResponse struct {
-	Credential credsdk.VerifiableCredential `json:"credential"`
+	Credential    *credsdk.VerifiableCredential `json:"credential,omitempty"`
+	CredentialJWT *string                       `json:"credentialJwt,omitempty"`
 }
 
 // CreateCredential godoc
@@ -92,13 +94,14 @@ func (cr CredentialRouter) CreateCredential(ctx context.Context, w http.Response
 		return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusInternalServerError)
 	}
 
-	resp := CreateCredentialResponse{Credential: createCredentialResponse.Credential}
+	resp := CreateCredentialResponse{Credential: createCredentialResponse.Credential, CredentialJWT: createCredentialResponse.CredentialJWT}
 	return framework.Respond(ctx, w, resp, http.StatusCreated)
 }
 
 type GetCredentialResponse struct {
-	ID         string                       `json:"id"`
-	Credential credsdk.VerifiableCredential `json:"credential"`
+	ID            string                        `json:"id"`
+	Credential    *credsdk.VerifiableCredential `json:"credential,omitempty"`
+	CredentialJWT *string                       `json:"credentialJwt,omitempty"`
 }
 
 // GetCredential godoc
@@ -127,14 +130,15 @@ func (cr CredentialRouter) GetCredential(ctx context.Context, w http.ResponseWri
 	}
 
 	resp := GetCredentialResponse{
-		ID:         gotCredential.Credential.ID,
-		Credential: gotCredential.Credential,
+		ID:            gotCredential.ID,
+		Credential:    gotCredential.Credential,
+		CredentialJWT: gotCredential.CredentialJWT,
 	}
 	return framework.Respond(ctx, w, resp, http.StatusOK)
 }
 
 type GetCredentialsResponse struct {
-	Credentials []credsdk.VerifiableCredential `json:"credentials"`
+	Credentials []credmodel.CredentialContainer `json:"credentials"`
 }
 
 // GetCredentials godoc
diff --git a/pkg/server/router/keystore.go b/pkg/server/router/keystore.go
index bb94ad3..d5c6214 100644
--- a/pkg/server/router/keystore.go
+++ b/pkg/server/router/keystore.go
@@ -42,11 +42,15 @@ func (sk StoreKeyRequest) ToServiceRequest() (*keystore.StoreKeyRequest, error)
 	if err != nil {
 		return nil, errors.Wrap(err, "could not decode base58 private key")
 	}
+	privKey, err := crypto.BytesToPrivKey(privateKeyBytes, sk.Type)
+	if err != nil {
+		return nil, errors.Wrap(err, "could not convert bytes to private key")
+	}
 	return &keystore.StoreKeyRequest{
 		ID:         sk.ID,
 		Type:       sk.Type,
 		Controller: sk.Controller,
-		Key:        privateKeyBytes,
+		Key:        privKey,
 	}, nil
 }
 
diff --git a/pkg/server/router/manifest.go b/pkg/server/router/manifest.go
index 39ba8b4..9a1170d 100644
--- a/pkg/server/router/manifest.go
+++ b/pkg/server/router/manifest.go
@@ -5,7 +5,6 @@ import (
 	"fmt"
 	"net/http"
 
-	"github.com/TBD54566975/ssi-sdk/credential"
 	manifestsdk "github.com/TBD54566975/ssi-sdk/credential/manifest"
 
 	"github.com/tbd54566975/ssi-service/pkg/service/manifest"
@@ -181,19 +180,20 @@ func (mr ManifestRouter) DeleteManifest(ctx context.Context, w http.ResponseWrit
 type SubmitApplicationRequest struct {
 	Application manifestsdk.CredentialApplication `json:"application" validate:"required"`
 	// Once we have JWT signed wrapper that can get the did this can be removed
-	RequesterDID string `json:"requesterDid" validate:"required"`
+	ApplicantDID string `json:"applicantDid" validate:"required"`
 }
 
 func (sar SubmitApplicationRequest) ToServiceRequest() manifest.SubmitApplicationRequest {
 	return manifest.SubmitApplicationRequest{
 		Application:  sar.Application,
-		RequesterDID: sar.RequesterDID,
+		ApplicantDID: sar.ApplicantDID,
 	}
 }
 
 type SubmitApplicationResponse struct {
-	Response    manifestsdk.CredentialResponse    `json:"response"`
-	Credentials []credential.VerifiableCredential `json:"credentials"`
+	Response manifestsdk.CredentialResponse `json:"credential_response"`
+	// this is an interface type to union Data Integrity and JWT style VCs
+	Credentials []interface{} `json:"verifiableCredential"`
 }
 
 // SubmitApplication godoc
@@ -223,7 +223,15 @@ func (mr ManifestRouter) SubmitApplication(ctx context.Context, w http.ResponseW
 		return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusInternalServerError)
 	}
 
-	resp := SubmitApplicationResponse{Response: submitApplicationResponse.Response, Credentials: submitApplicationResponse.Credential}
+	var credentials []interface{}
+	for _, container := range submitApplicationResponse.Credential {
+		if container.HasDataIntegrityCredential() {
+			credentials = append(credentials, *container.Credential)
+		} else if container.HasJWTCredential() {
+			credentials = append(credentials, *container.CredentialJWT)
+		}
+	}
+	resp := SubmitApplicationResponse{Response: submitApplicationResponse.Response, Credentials: credentials}
 
 	return framework.Respond(ctx, w, resp, http.StatusCreated)
 }
diff --git a/pkg/service/credential/credential.go b/pkg/service/credential/credential.go
index 9da063f..2e6071d 100644
--- a/pkg/service/credential/credential.go
+++ b/pkg/service/credential/credential.go
@@ -5,9 +5,12 @@ import (
 	"time"
 
 	"github.com/TBD54566975/ssi-sdk/credential"
+	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 
 	"github.com/tbd54566975/ssi-service/config"
+	credmodel "github.com/tbd54566975/ssi-service/internal/credential"
+	"github.com/tbd54566975/ssi-service/internal/keyaccess"
 	"github.com/tbd54566975/ssi-service/internal/util"
 	credstorage "github.com/tbd54566975/ssi-service/pkg/service/credential/storage"
 	"github.com/tbd54566975/ssi-service/pkg/service/framework"
@@ -119,14 +122,21 @@ func (s Service) CreateCredential(request CreateCredentialRequest) (*CreateCrede
 		return nil, util.LoggingErrorMsg(err, errMsg)
 	}
 
+	// TODO(gabe) support Data Integrity creds too https://github.com/TBD54566975/ssi-service/issues/105
+	// sign the credential
+	credJWT, err := s.signCredentialJWT(request.Issuer, *cred)
+	if err != nil {
+		return nil, util.LoggingErrorMsg(err, "could not sign credential")
+	}
+
 	// store the credential
-	storageRequest := credstorage.StoredCredential{
-		ID:           cred.ID,
-		Credential:   *cred,
-		Issuer:       request.Issuer,
-		Subject:      request.Subject,
-		Schema:       request.JSONSchema,
-		IssuanceDate: cred.IssuanceDate,
+	container := credmodel.CredentialContainer{
+		ID:            cred.ID,
+		Credential:    cred,
+		CredentialJWT: credJWT,
+	}
+	storageRequest := credstorage.StoreCredentialRequest{
+		CredentialContainer: container,
 	}
 	if err := s.storage.StoreCredential(storageRequest); err != nil {
 		errMsg := "could not store credential"
@@ -134,10 +144,30 @@ func (s Service) CreateCredential(request CreateCredentialRequest) (*CreateCrede
 	}
 
 	// return the result
-	response := CreateCredentialResponse{Credential: *cred}
+	response := CreateCredentialResponse{CredentialContainer: container}
 	return &response, nil
 }
 
+// signCredentialJWT signs a credential and returns it as a vc-jwt
+func (s Service) signCredentialJWT(issuer string, cred credential.VerifiableCredential) (*string, error) {
+	gotKey, err := s.keyStore.GetKey(keystore.GetKeyRequest{ID: issuer})
+	if err != nil {
+		errMsg := fmt.Sprintf("could not get key for signing credential with key<%s>", issuer)
+		return nil, util.LoggingErrorMsg(err, errMsg)
+	}
+	keyAccess, err := keyaccess.NewJWKKeyAccess(gotKey.ID, gotKey.Key)
+	if err != nil {
+		errMsg := fmt.Sprintf("could not create key access for signing credential with key<%s>", issuer)
+		return nil, errors.Wrap(err, errMsg)
+	}
+	credToken, err := keyAccess.SignVerifiableCredential(cred)
+	if err != nil {
+		errMsg := fmt.Sprintf("could not sign credential with key<%s>", issuer)
+		return nil, errors.Wrap(err, errMsg)
+	}
+	return &credToken.Token, nil
+}
+
 func (s Service) GetCredential(request GetCredentialRequest) (*GetCredentialResponse, error) {
 
 	logrus.Debugf("getting credential: %s", request.ID)
@@ -147,8 +177,17 @@ func (s Service) GetCredential(request GetCredentialRequest) (*GetCredentialResp
 		errMsg := fmt.Sprintf("could not get credential: %s", request.ID)
 		return nil, util.LoggingErrorMsg(err, errMsg)
 	}
-
-	response := GetCredentialResponse{Credential: gotCred.Credential}
+	if !gotCred.IsValid() {
+		errMsg := fmt.Sprintf("credential returned is not valid: %s", request.ID)
+		return nil, util.LoggingNewError(errMsg)
+	}
+	response := GetCredentialResponse{
+		credmodel.CredentialContainer{
+			ID:            gotCred.CredentialID,
+			Credential:    gotCred.Credential,
+			CredentialJWT: gotCred.CredentialJWT,
+		},
+	}
 	return &response, nil
 }
 
@@ -162,9 +201,14 @@ func (s Service) GetCredentialsByIssuer(request GetCredentialByIssuerRequest) (*
 		return nil, util.LoggingErrorMsg(err, errMsg)
 	}
 
-	var creds []credential.VerifiableCredential
+	var creds []credmodel.CredentialContainer
 	for _, cred := range gotCreds {
-		creds = append(creds, cred.Credential)
+		container := credmodel.CredentialContainer{
+			ID:            cred.CredentialID,
+			Credential:    cred.Credential,
+			CredentialJWT: cred.CredentialJWT,
+		}
+		creds = append(creds, container)
 	}
 
 	response := GetCredentialsResponse{Credentials: creds}
@@ -181,11 +225,15 @@ func (s Service) GetCredentialsBySubject(request GetCredentialBySubjectRequest)
 		return nil, util.LoggingErrorMsg(err, errMsg)
 	}
 
-	var creds []credential.VerifiableCredential
+	var creds []credmodel.CredentialContainer
 	for _, cred := range gotCreds {
-		creds = append(creds, cred.Credential)
+		container := credmodel.CredentialContainer{
+			ID:            cred.CredentialID,
+			Credential:    cred.Credential,
+			CredentialJWT: cred.CredentialJWT,
+		}
+		creds = append(creds, container)
 	}
-
 	response := GetCredentialsResponse{Credentials: creds}
 	return &response, nil
 }
@@ -200,11 +248,15 @@ func (s Service) GetCredentialsBySchema(request GetCredentialBySchemaRequest) (*
 		return nil, util.LoggingErrorMsg(err, errMsg)
 	}
 
-	var creds []credential.VerifiableCredential
+	var creds []credmodel.CredentialContainer
 	for _, cred := range gotCreds {
-		creds = append(creds, cred.Credential)
+		container := credmodel.CredentialContainer{
+			ID:            cred.CredentialID,
+			Credential:    cred.Credential,
+			CredentialJWT: cred.CredentialJWT,
+		}
+		creds = append(creds, container)
 	}
-
 	response := GetCredentialsResponse{Credentials: creds}
 	return &response, nil
 }
diff --git a/pkg/service/credential/model.go b/pkg/service/credential/model.go
index f404407..dfeac00 100644
--- a/pkg/service/credential/model.go
+++ b/pkg/service/credential/model.go
@@ -1,7 +1,7 @@
 package credential
 
 import (
-	credsdk "github.com/TBD54566975/ssi-sdk/credential"
+	"github.com/tbd54566975/ssi-service/internal/credential"
 )
 
 const (
@@ -20,8 +20,10 @@ type CreateCredentialRequest struct {
 	// TODO(gabe) support more capabilities like signature type, format, status, and more.
 }
 
+// CreateCredentialResponse holds a resulting credential from credential creation, which is an XOR type:
+// containing either a Data Integrity Proofed credential or a VC-JWT representation.
 type CreateCredentialResponse struct {
-	Credential credsdk.VerifiableCredential
+	credential.CredentialContainer
 }
 
 type GetCredentialRequest struct {
@@ -29,7 +31,7 @@ type GetCredentialRequest struct {
 }
 
 type GetCredentialResponse struct {
-	Credential credsdk.VerifiableCredential
+	credential.CredentialContainer
 }
 
 type GetCredentialByIssuerRequest struct {
@@ -45,7 +47,7 @@ type GetCredentialBySchemaRequest struct {
 }
 
 type GetCredentialsResponse struct {
-	Credentials []credsdk.VerifiableCredential
+	Credentials []credential.CredentialContainer
 }
 
 type DeleteCredentialRequest struct {
diff --git a/pkg/service/credential/storage/bolt.go b/pkg/service/credential/storage/bolt.go
index c43fa92..8ce2f48 100644
--- a/pkg/service/credential/storage/bolt.go
+++ b/pkg/service/credential/storage/bolt.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"strings"
 
+	"github.com/TBD54566975/ssi-sdk/credential/signing"
 	"github.com/goccy/go-json"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
@@ -28,21 +29,60 @@ func NewBoltCredentialStorage(db *storage.BoltDB) (*BoltCredentialStorage, error
 	return &BoltCredentialStorage{db: db}, nil
 }
 
-func (b BoltCredentialStorage) StoreCredential(credential StoredCredential) error {
-	id := credential.Credential.ID
-	if id == "" {
-		return util.LoggingNewError("could not store credential without an ID")
+func (b BoltCredentialStorage) StoreCredential(request StoreCredentialRequest) error {
+	if !request.IsValid() {
+		return util.LoggingNewError("store request request is not valid")
 	}
 
-	// create and set prefix key for the credential
-	credential.ID = createPrefixKey(id, credential.Issuer, credential.Subject, credential.Schema)
+	// transform the credential into its denormalized form for storage
+	storedCredential, err := buildStoredCredential(request)
+	if err != nil {
+		return errors.Wrap(err, "could not build stored credential")
+	}
 
-	credBytes, err := json.Marshal(credential)
+	storedCredBytes, err := json.Marshal(storedCredential)
 	if err != nil {
-		errMsg := fmt.Sprintf("could not store credential: %s", id)
+		errMsg := fmt.Sprintf("could not store request: %s", storedCredential.CredentialID)
 		return util.LoggingErrorMsg(err, errMsg)
 	}
-	return b.db.Write(namespace, credential.ID, credBytes)
+	// TODO(gabe) conflict checking?
+	return b.db.Write(namespace, storedCredential.ID, storedCredBytes)
+}
+
+// buildStoredCredential generically parses a store credential request and returns the object to be stored
+func buildStoredCredential(request StoreCredentialRequest) (*StoredCredential, error) {
+	// assume we have a Data Integrity credential
+	cred := request.Credential
+	if request.HasJWTCredential() {
+		parsedCred, err := signing.ParseVerifiableCredentialFromJWT(*request.CredentialJWT)
+		if err != nil {
+			return nil, errors.Wrap(err, "could not parse credential from jwt")
+		}
+
+		// if we have a JWT credential, update the reference
+		cred = parsedCred
+	}
+
+	credID := cred.ID
+	// Note: we assume the issuer is always a string for now
+	issuer := cred.Issuer.(string)
+	subject := cred.CredentialSubject.GetID()
+
+	// schema is not a required field, so we must do this check
+	schema := ""
+	if cred.CredentialSchema != nil {
+		schema = cred.CredentialSchema.ID
+	}
+	return &StoredCredential{
+		ID:            createPrefixKey(credID, issuer, subject, schema),
+		CredentialID:  credID,
+		Credential:    cred,
+		CredentialJWT: request.CredentialJWT,
+		Issuer:        issuer,
+		Subject:       subject,
+		Schema:        schema,
+		IssuanceDate:  cred.IssuanceDate,
+	}, nil
 }
 
 func (b BoltCredentialStorage) GetCredential(id string) (*StoredCredential, error) {
diff --git a/pkg/service/credential/storage/storage.go b/pkg/service/credential/storage/storage.go
index be26bca..24c976d 100644
--- a/pkg/service/credential/storage/storage.go
+++ b/pkg/service/credential/storage/storage.go
@@ -5,21 +5,45 @@ import (
 
 	"github.com/TBD54566975/ssi-sdk/credential"
 
+	cred "github.com/tbd54566975/ssi-service/internal/credential"
 	"github.com/tbd54566975/ssi-service/internal/util"
 	"github.com/tbd54566975/ssi-service/pkg/storage"
 )
 
+type StoreCredentialRequest struct {
+	cred.CredentialContainer
+}
+
 type StoredCredential struct {
-	ID           string                          `json:"id"`
-	Credential   credential.VerifiableCredential `json:"credential"`
-	Issuer       string                          `json:"issuer"`
-	Subject      string                          `json:"subject"`
-	Schema       string                          `json:"schema"`
-	IssuanceDate string                          `json:"issuanceDate"`
+	// This ID is generated by the storage module upon first write
+	ID string `json:"id"`
+
+	CredentialID string `json:"credentialId"`
+
+	// only one of these fields should be present
+	Credential    *credential.VerifiableCredential `json:"credential,omitempty"`
+	CredentialJWT *string                          `json:"token,omitempty"`
+
+	Issuer       string `json:"issuer"`
+	Subject      string `json:"subject"`
+	Schema       string `json:"schema"`
+	IssuanceDate string `json:"issuanceDate"`
+}
+
+func (sc StoredCredential) IsValid() bool {
+	return sc.ID != "" && (sc.HasDataIntegrityCredential() || sc.HasJWTCredential())
+}
+
+func (sc StoredCredential) HasDataIntegrityCredential() bool {
+	return sc.Credential != nil && sc.Credential.Proof != nil
+}
+
+func (sc StoredCredential) HasJWTCredential() bool {
+	return sc.CredentialJWT != nil
 }
 
 type Storage interface {
-	StoreCredential(credential StoredCredential) error
+	StoreCredential(request StoreCredentialRequest) error
 	GetCredential(id string) (*StoredCredential, error)
 	GetCredentialsByIssuer(issuer string) ([]StoredCredential, error)
 	GetCredentialsBySubject(subject string) ([]StoredCredential, error)
diff --git a/pkg/service/keystore/keystore.go b/pkg/service/keystore/keystore.go
index 77b7f72..ae54db4 100644
--- a/pkg/service/keystore/keystore.go
+++ b/pkg/service/keystore/keystore.go
@@ -71,11 +71,18 @@ func (s Service) StoreKey(request StoreKeyRequest) error {
 		return util.LoggingNewError(errMsg)
 	}
 
+	// serialize the key before storage
+	keyBytes, err := crypto.PrivKeyToBytes(request.Key)
+	if err != nil {
+		return errors.Wrap(err, "could not serialize key before storage")
+	}
+	privKeyBase58 := base58.Encode(keyBytes)
+
 	key := keystorestorage.StoredKey{
 		ID:         request.ID,
 		Controller: request.Controller,
 		KeyType:    request.Type,
-		Key:        request.Key,
+		Base58Key:  privKeyBase58,
 		CreatedAt:  time.Now().Format(time.RFC3339),
 	}
 	if err := s.storage.StoreKey(key); err != nil {
@@ -99,11 +106,22 @@ func (s Service) GetKey(request GetKeyRequest) (*GetKeyResponse, error) {
 		err := errors.Wrapf(err, "key with id<%s> could not be found", id)
 		return nil, util.LoggingError(err)
 	}
+
+	// deserialize the key before returning
+	keyBytes, err := base58.Decode(gotKey.Base58Key)
+	if err != nil {
+		return nil, errors.Wrap(err, "could not deserialize key from base58")
+	}
+	privKey, err := crypto.BytesToPrivKey(keyBytes, gotKey.KeyType)
+	if err != nil {
+		return nil, errors.Wrap(err, "could not reconstruct private key from storage")
+	}
+
 	return &GetKeyResponse{
 		ID:         gotKey.ID,
 		Type:       gotKey.KeyType,
 		Controller: gotKey.Controller,
-		Key:        gotKey.Key,
+		Key:        privKey,
 		CreatedAt:  gotKey.CreatedAt,
 	}, nil
 }
diff --git a/pkg/service/keystore/storage/storage.go b/pkg/service/keystore/storage/storage.go
index 226bd0b..37bd508 100644
--- a/pkg/service/keystore/storage/storage.go
+++ b/pkg/service/keystore/storage/storage.go
@@ -1,7 +1,6 @@
 package storage
 
 import (
-	gocrypto "crypto"
 	"fmt"
 
 	"github.com/TBD54566975/ssi-sdk/crypto"
@@ -12,11 +11,11 @@ import (
 
 // StoredKey represents a common data model to store data on all key types
 type StoredKey struct {
-	ID         string              `json:"id"`
-	Controller string              `json:"controller"`
-	KeyType    crypto.KeyType      `json:"keyType"`
-	Key        gocrypto.PrivateKey `json:"key"`
-	CreatedAt  string              `json:"createdAt"`
+	ID         string         `json:"id"`
+	Controller string         `json:"controller"`
+	KeyType    crypto.KeyType `json:"keyType"`
+	Base58Key  string         `json:"key"`
+	CreatedAt  string         `json:"createdAt"`
 }
 
 // KeyDetails represents a common data model to get information about a key, without revealing the key itself
diff --git a/pkg/service/manifest/manifest.go b/pkg/service/manifest/manifest.go
index e06dfcb..17e21bc 100644
--- a/pkg/service/manifest/manifest.go
+++ b/pkg/service/manifest/manifest.go
@@ -3,12 +3,12 @@ package manifest
 import (
 	"fmt"
 
-	credsdk "github.com/TBD54566975/ssi-sdk/credential"
 	"github.com/TBD54566975/ssi-sdk/credential/exchange"
 	"github.com/TBD54566975/ssi-sdk/credential/manifest"
 	"github.com/sirupsen/logrus"
 
 	"github.com/tbd54566975/ssi-service/config"
+	cred "github.com/tbd54566975/ssi-service/internal/credential"
 	"github.com/tbd54566975/ssi-service/internal/util"
 	"github.com/tbd54566975/ssi-service/pkg/service/credential"
 	"github.com/tbd54566975/ssi-service/pkg/service/framework"
@@ -124,27 +124,6 @@ func (s Service) DeleteManifest(request DeleteManifestRequest) error {
 	return nil
 }
 
-// TODO: (Neal) Add entire validation framework in place of these validation checks - https://github.com/TBD54566975/ssi-service/issues/95
-func isValidApplication(gotManifest *manifeststorage.StoredManifest, application manifest.CredentialApplication) error {
-	if gotManifest == nil {
-		return util.LoggingNewError(fmt.Sprintf("application is not valid. A manifest does not exist with id: %s", application.ManifestID))
-	}
-
-	inputDescriptors := gotManifest.Manifest.PresentationDefinition.InputDescriptors
-	inputDescriptorIDs := make(map[string]bool)
-	for _, inputDescriptor := range inputDescriptors {
-		inputDescriptorIDs[inputDescriptor.ID] = true
-	}
-
-	for _, submissionDescriptor := range application.PresentationSubmission.DescriptorMap {
-		if inputDescriptorIDs[submissionDescriptor.ID] != true {
-			return util.LoggingNewError("application is not valid. The submission descriptor ids do not match the input descriptor ids")
-		}
-	}
-
-	return nil
-}
-
 func (s Service) ProcessApplicationSubmission(request SubmitApplicationRequest) (*SubmitApplicationResponse, error) {
 	credApp := request.Application
 
@@ -181,11 +160,11 @@ func (s Service) ProcessApplicationSubmission(request SubmitApplicationRequest)
 		return nil, util.LoggingErrorMsg(err, "could not fulfill credential application: could not set application id")
 	}
 
-	var creds []credsdk.VerifiableCredential
+	var creds []cred.CredentialContainer
 	for _, od := range gotManifest.Manifest.OutputDescriptors {
 		credentialRequest := credential.CreateCredentialRequest{
 			Issuer:     gotManifest.Manifest.Issuer.ID,
-			Subject:    request.RequesterDID,
+			Subject:    request.ApplicantDID,
 			JSONSchema: od.Schema,
 			// TODO(gabe) need to add in data here to match the request + schema
 			Data: map[string]interface{}{},
@@ -196,15 +175,22 @@ func (s Service) ProcessApplicationSubmission(request SubmitApplicationRequest)
 			return nil, util.LoggingErrorMsg(err, "could not create credential")
 		}
 
-		creds = append(creds, credentialResponse.Credential)
+		creds = append(creds, credentialResponse.CredentialContainer)
 	}
 
+	// build descriptor map based on credential type
 	var descriptors []exchange.SubmissionDescriptor
-	for i, cred := range creds {
-		// TODO(gabe) build this correctly based on the generated credential format and envelope type
+	for i, c := range creds {
+		var format string
+		if c.HasDataIntegrityCredential() {
+			format = string(exchange.LDPVC)
+		}
+		if c.HasJWTCredential() {
+			format = string(exchange.JWTVC)
+		}
 		descriptors = append(descriptors, exchange.SubmissionDescriptor{
-			ID:     cred.ID,
-			Format: string(exchange.JWTVC),
+			ID:     c.ID,
+			Format: format,
 			Path:   fmt.Sprintf("$.verifiableCredential[%d]", i),
 		})
 	}
@@ -232,6 +218,27 @@ func (s Service) ProcessApplicationSubmission(request SubmitApplicationRequest)
 	return &response, nil
 }
 
+// TODO: (Neal) Add entire validation framework in place of these validation checks - https://github.com/TBD54566975/ssi-service/issues/95
+func isValidApplication(gotManifest *manifeststorage.StoredManifest, application manifest.CredentialApplication) error {
+	if gotManifest == nil {
+		return util.LoggingNewError(fmt.Sprintf("application is not valid. A manifest does not exist with id: %s", application.ManifestID))
+	}
+
+	inputDescriptors := gotManifest.Manifest.PresentationDefinition.InputDescriptors
+	inputDescriptorIDs := make(map[string]bool)
+	for _, inputDescriptor := range inputDescriptors {
+		inputDescriptorIDs[inputDescriptor.ID] = true
+	}
+
+	for _, submissionDescriptor := range application.PresentationSubmission.DescriptorMap {
+		if inputDescriptorIDs[submissionDescriptor.ID] != true {
+			return util.LoggingNewError("application is not valid. The submission descriptor ids do not match the input descriptor ids")
+		}
+	}
+
+	return nil
+}
+
 func (s Service) GetApplication(request GetApplicationRequest) (*GetApplicationResponse, error) {
 
 	logrus.Debugf("getting application: %s", request.ID)
diff --git a/pkg/service/manifest/model.go b/pkg/service/manifest/model.go
index b8f971d..79ddb86 100644
--- a/pkg/service/manifest/model.go
+++ b/pkg/service/manifest/model.go
@@ -1,11 +1,13 @@
 package manifest
 
 import (
-	"github.com/TBD54566975/ssi-sdk/credential"
 	manifestsdk "github.com/TBD54566975/ssi-sdk/credential/manifest"
+
+	cred "github.com/tbd54566975/ssi-service/internal/credential"
 )
 
 // Manifest
+
 type CreateManifestRequest struct {
 	Manifest manifestsdk.CredentialManifest
 }
@@ -31,14 +33,15 @@ type DeleteManifestRequest struct {
 }
 
 // Application
+
 type SubmitApplicationRequest struct {
 	Application  manifestsdk.CredentialApplication
-	RequesterDID string
+	ApplicantDID string
 }
 
 type SubmitApplicationResponse struct {
 	Response   manifestsdk.CredentialResponse
-	Credential []credential.VerifiableCredential
+	Credential []cred.CredentialContainer
 }
 
 type GetApplicationRequest struct {
@@ -58,6 +61,7 @@ type DeleteApplicationRequest struct {
 }
 
 // Response
+
 type GetResponseRequest struct {
 	ID string
 }


Test patch:
diff --git a/pkg/server/router/credential_test.go b/pkg/server/router/credential_test.go
index 81dc1dd..34a9cfe 100644
--- a/pkg/server/router/credential_test.go
+++ b/pkg/server/router/credential_test.go
@@ -7,11 +7,12 @@ import (
 	"time"
 
 	credsdk "github.com/TBD54566975/ssi-sdk/credential"
-	"github.com/goccy/go-json"
+	"github.com/TBD54566975/ssi-sdk/crypto"
 	"github.com/stretchr/testify/assert"
 
 	"github.com/tbd54566975/ssi-service/config"
 	"github.com/tbd54566975/ssi-service/pkg/service/credential"
+	"github.com/tbd54566975/ssi-service/pkg/service/did"
 	"github.com/tbd54566975/ssi-service/pkg/service/framework"
 	"github.com/tbd54566975/ssi-service/pkg/storage"
 )
@@ -43,6 +44,7 @@ func TestCredentialRouter(t *testing.T) {
 
 		serviceConfig := config.CredentialServiceConfig{BaseServiceConfig: &config.BaseServiceConfig{Name: "credential"}}
 		keyStoreService := testKeyStoreService(tt, bolt)
+		didService := testDIDService(tt, bolt, keyStoreService)
 		credService, err := credential.NewCredentialService(serviceConfig, bolt, keyStoreService)
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, credService)
@@ -52,7 +54,12 @@ func TestCredentialRouter(t *testing.T) {
 		assert.Equal(tt, framework.StatusReady, credService.Status().Status)
 
 		// create a credential
-		issuer := "did:test:123"
+
+		issuerDID, err := didService.CreateDIDByMethod(did.CreateDIDRequest{Method: did.KeyMethod, KeyType: crypto.Ed25519})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, issuerDID)
+
+		issuer := issuerDID.DID.ID
 		subject := "did:test:345"
 		createdCred, err := credService.CreateCredential(credential.CreateCredentialRequest{
 			Issuer:  issuer,
@@ -65,25 +72,23 @@ func TestCredentialRouter(t *testing.T) {
 		})
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, createdCred)
-		assert.NotEmpty(tt, createdCred.Credential)
+		assert.NotEmpty(tt, createdCred.CredentialJWT)
+
+		cred := createdCred.Credential
 
 		// make sure it has the right data
-		assert.Equal(tt, issuer, createdCred.Credential.Issuer)
-		assert.Equal(tt, subject, createdCred.Credential.CredentialSubject[credsdk.VerifiableCredentialIDProperty])
-		assert.Equal(tt, "Satoshi", createdCred.Credential.CredentialSubject["firstName"])
-		assert.Equal(tt, "Nakamoto", createdCred.Credential.CredentialSubject["lastName"])
+		assert.Equal(tt, issuer, cred.Issuer)
+		assert.Equal(tt, subject, cred.CredentialSubject[credsdk.VerifiableCredentialIDProperty])
+		assert.Equal(tt, "Satoshi", cred.CredentialSubject["firstName"])
+		assert.Equal(tt, "Nakamoto", cred.CredentialSubject["lastName"])
 
 		// get it back
-		gotCred, err := credService.GetCredential(credential.GetCredentialRequest{ID: createdCred.Credential.ID})
+		gotCred, err := credService.GetCredential(credential.GetCredentialRequest{ID: cred.ID})
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, gotCred)
 
-		// marshal to JSON and compare for object equality
-		createdBytes, err := json.Marshal(createdCred.Credential)
-		assert.NoError(tt, err)
-		gotBytes, err := json.Marshal(gotCred.Credential)
-		assert.NoError(tt, err)
-		assert.Equal(tt, createdBytes, gotBytes)
+		// compare for object equality
+		assert.Equal(tt, createdCred.CredentialJWT, gotCred.CredentialJWT)
 
 		// get a cred that doesn't exist
 		_, err = credService.GetCredential(credential.GetCredentialRequest{ID: "bad"})
@@ -94,22 +99,23 @@ func TestCredentialRouter(t *testing.T) {
 		bySchema, err := credService.GetCredentialsBySchema(credential.GetCredentialBySchemaRequest{Schema: ""})
 		assert.NoError(tt, err)
 		assert.Len(tt, bySchema.Credentials, 1)
-		assert.Equal(tt, bySchema.Credentials[0].ID, createdCred.Credential.ID)
-		assert.EqualValues(tt, bySchema.Credentials[0].CredentialSchema, createdCred.Credential.CredentialSchema)
+		assert.EqualValues(tt, cred.CredentialSchema, bySchema.Credentials[0].Credential.CredentialSchema)
 
 		// get by subject
 		bySubject, err := credService.GetCredentialsBySubject(credential.GetCredentialBySubjectRequest{Subject: subject})
 		assert.NoError(tt, err)
 		assert.Len(tt, bySubject.Credentials, 1)
-		assert.Equal(tt, bySubject.Credentials[0].ID, createdCred.Credential.ID)
-		assert.Equal(tt, bySubject.Credentials[0].CredentialSubject[credsdk.VerifiableCredentialIDProperty], createdCred.Credential.CredentialSubject[credsdk.VerifiableCredentialIDProperty])
+
+		assert.Equal(tt, cred.ID, bySubject.Credentials[0].ID)
+		assert.Equal(tt, cred.CredentialSubject[credsdk.VerifiableCredentialIDProperty], bySubject.Credentials[0].Credential.CredentialSubject[credsdk.VerifiableCredentialIDProperty])
 
 		// get by issuer
 		byIssuer, err := credService.GetCredentialsByIssuer(credential.GetCredentialByIssuerRequest{Issuer: issuer})
 		assert.NoError(tt, err)
 		assert.Len(tt, byIssuer.Credentials, 1)
-		assert.Equal(tt, byIssuer.Credentials[0].ID, createdCred.Credential.ID)
-		assert.Equal(tt, byIssuer.Credentials[0].Issuer, createdCred.Credential.Issuer)
+
+		assert.Equal(tt, cred.ID, byIssuer.Credentials[0].Credential.ID)
+		assert.Equal(tt, cred.Issuer, byIssuer.Credentials[0].Credential.Issuer)
 
 		// create another cred with the same issuer, different subject, different schema
 		anotherCreatedCred, err := credService.CreateCredential(credential.CreateCredentialRequest{
@@ -133,26 +139,28 @@ func TestCredentialRouter(t *testing.T) {
 		bySchema, err = credService.GetCredentialsBySchema(credential.GetCredentialBySchemaRequest{Schema: ""})
 		assert.NoError(tt, err)
 		assert.Len(tt, bySchema.Credentials, 1)
-		assert.Equal(tt, bySchema.Credentials[0].ID, createdCred.Credential.ID)
-		assert.EqualValues(tt, bySchema.Credentials[0].CredentialSchema, createdCred.Credential.CredentialSchema)
+
+		assert.Equal(tt, cred.ID, bySchema.Credentials[0].ID)
+		assert.EqualValues(tt, cred.CredentialSchema, bySchema.Credentials[0].Credential.CredentialSchema)
 
 		bySubject, err = credService.GetCredentialsBySubject(credential.GetCredentialBySubjectRequest{Subject: subject})
 		assert.NoError(tt, err)
 		assert.Len(tt, bySubject.Credentials, 1)
-		assert.Equal(tt, bySubject.Credentials[0].ID, createdCred.Credential.ID)
-		assert.Equal(tt, bySubject.Credentials[0].CredentialSubject[credsdk.VerifiableCredentialIDProperty], createdCred.Credential.CredentialSubject[credsdk.VerifiableCredentialIDProperty])
+
+		assert.Equal(tt, cred.ID, bySubject.Credentials[0].ID)
+		assert.Equal(tt, cred.CredentialSubject[credsdk.VerifiableCredentialIDProperty], bySubject.Credentials[0].Credential.CredentialSubject[credsdk.VerifiableCredentialIDProperty])
 
 		// delete a cred that doesn't exist (no error since idempotent)
 		err = credService.DeleteCredential(credential.DeleteCredentialRequest{ID: "bad"})
 		assert.NoError(tt, err)
 
 		// delete a credential that does exist
-		err = credService.DeleteCredential(credential.DeleteCredentialRequest{ID: createdCred.Credential.ID})
+		err = credService.DeleteCredential(credential.DeleteCredentialRequest{ID: cred.ID})
 		assert.NoError(tt, err)
 
 		// get it back
-		_, err = credService.GetCredential(credential.GetCredentialRequest{ID: createdCred.Credential.ID})
+		_, err = credService.GetCredential(credential.GetCredentialRequest{ID: cred.ID})
 		assert.Error(tt, err)
-		assert.Contains(tt, err.Error(), fmt.Sprintf("credential not found with id: %s", createdCred.Credential.ID))
+		assert.Contains(tt, err.Error(), fmt.Sprintf("credential not found with id: %s", cred.ID))
 	})
 }
diff --git a/pkg/server/router/keystore_test.go b/pkg/server/router/keystore_test.go
index a5bb87e..a0636bc 100644
--- a/pkg/server/router/keystore_test.go
+++ b/pkg/server/router/keystore_test.go
@@ -66,15 +66,12 @@ func TestKeyStoreRouter(t *testing.T) {
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, privKey)
 
-		privKeyBytes, err := crypto.PrivKeyToBytes(privKey)
-		assert.NoError(tt, err)
-
 		keyID := "did:test:me#key-1"
 		err = keyStoreService.StoreKey(keystore.StoreKeyRequest{
 			ID:         keyID,
 			Type:       crypto.Ed25519,
 			Controller: "did:test:me",
-			Key:        privKeyBytes,
+			Key:        privKey,
 		})
 		assert.NoError(tt, err)
 
diff --git a/pkg/server/router/manifest_test.go b/pkg/server/router/manifest_test.go
index 0ec97dd..348f766 100644
--- a/pkg/server/router/manifest_test.go
+++ b/pkg/server/router/manifest_test.go
@@ -11,6 +11,7 @@ import (
 	"github.com/stretchr/testify/assert"
 
 	"github.com/tbd54566975/ssi-service/config"
+	"github.com/tbd54566975/ssi-service/pkg/service/did"
 	"github.com/tbd54566975/ssi-service/pkg/service/framework"
 	"github.com/tbd54566975/ssi-service/pkg/service/manifest"
 	"github.com/tbd54566975/ssi-service/pkg/storage"
@@ -52,8 +53,22 @@ func TestManifestRouter(t *testing.T) {
 		assert.Equal(tt, framework.Manifest, manifestService.Type())
 		assert.Equal(tt, framework.StatusReady, manifestService.Status().Status)
 
+		// create issuer and applicant DIDs
+		didService := testDIDService(tt, bolt, keyStoreService)
+		createDIDRequest := did.CreateDIDRequest{
+			Method:  did.KeyMethod,
+			KeyType: crypto.Ed25519,
+		}
+		issuerDID, err := didService.CreateDIDByMethod(createDIDRequest)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, issuerDID)
+
+		applicantDID, err := didService.CreateDIDByMethod(createDIDRequest)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, applicantDID)
+
 		// good manifest request
-		createManifestRequest := getValidManifestRequest()
+		createManifestRequest := getValidManifestRequest(issuerDID.DID.ID)
 
 		createdManifest, err := manifestService.CreateManifest(createManifestRequest)
 		assert.NoError(tt, err)
@@ -61,23 +76,25 @@ func TestManifestRouter(t *testing.T) {
 		assert.NotEmpty(tt, createdManifest.Manifest)
 
 		// good application request
-		createApplicationRequest := getValidApplicationRequest(createdManifest.Manifest.ID, createManifestRequest.Manifest.PresentationDefinition.InputDescriptors[0].ID)
+		createApplicationRequest := getValidApplicationRequest(applicantDID.DID.ID, createdManifest.Manifest.ID, createManifestRequest.Manifest.PresentationDefinition.InputDescriptors[0].ID)
+
+		createdApplicationResponse, err := manifestService.ProcessApplicationSubmission(createApplicationRequest)
 
-		createdApplication, err := manifestService.ProcessApplicationSubmission(createApplicationRequest)
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, createdManifest)
-		assert.NotEmpty(tt, createdApplication.Response.ID)
+		assert.NotEmpty(tt, createdApplicationResponse.Response.ID)
+		assert.Equal(tt, len(createManifestRequest.Manifest.OutputDescriptors), len(createdApplicationResponse.Credential))
 	})
 
 }
 
-func getValidManifestRequest() manifest.CreateManifestRequest {
+func getValidManifestRequest(issuerDID string) manifest.CreateManifestRequest {
 	createManifestRequest := manifest.CreateManifestRequest{
 		Manifest: manifestsdk.CredentialManifest{
 			ID:          "WA-DL-CLASS-A",
 			SpecVersion: "https://identity.foundation/credential-manifest/spec/v1.0.0/",
 			Issuer: manifestsdk.Issuer{
-				ID: "did:abc:123",
+				ID: issuerDID,
 			},
 			PresentationDefinition: &exchange.PresentationDefinition{
 				ID: "pres-def-id",
@@ -114,7 +131,7 @@ func getValidManifestRequest() manifest.CreateManifestRequest {
 	return createManifestRequest
 }
 
-func getValidApplicationRequest(manifestID string, submissionDescriptorId string) manifest.SubmitApplicationRequest {
+func getValidApplicationRequest(applicantDID, manifestID, submissionDescriptorID string) manifest.SubmitApplicationRequest {
 	createApplication := manifestsdk.CredentialApplication{
 		ID:          uuid.New().String(),
 		SpecVersion: "https://identity.foundation/credential-manifest/spec/v1.0.0/",
@@ -127,7 +144,7 @@ func getValidApplicationRequest(manifestID string, submissionDescriptorId string
 			DefinitionID: "definitionId",
 			DescriptorMap: []exchange.SubmissionDescriptor{
 				{
-					ID:     submissionDescriptorId,
+					ID:     submissionDescriptorID,
 					Format: "jwt",
 					Path:   "path",
 				},
@@ -137,7 +154,7 @@ func getValidApplicationRequest(manifestID string, submissionDescriptorId string
 
 	createApplicationRequest := manifest.SubmitApplicationRequest{
 		Application:  createApplication,
-		RequesterDID: "did:user:123",
+		ApplicantDID: applicantDID,
 	}
 
 	return createApplicationRequest
diff --git a/pkg/server/router/testutils_test.go b/pkg/server/router/testutils_test.go
index e43795d..1a052a6 100644
--- a/pkg/server/router/testutils_test.go
+++ b/pkg/server/router/testutils_test.go
@@ -7,6 +7,7 @@ import (
 
 	"github.com/tbd54566975/ssi-service/config"
 	"github.com/tbd54566975/ssi-service/pkg/service/credential"
+	"github.com/tbd54566975/ssi-service/pkg/service/did"
 	"github.com/tbd54566975/ssi-service/pkg/service/keystore"
 	"github.com/tbd54566975/ssi-service/pkg/service/manifest"
 	"github.com/tbd54566975/ssi-service/pkg/storage"
@@ -21,6 +22,15 @@ func testKeyStoreService(t *testing.T, db *storage.BoltDB) *keystore.Service {
 	return keystoreService
 }
 
+func testDIDService(t *testing.T, db *storage.BoltDB, keyStore *keystore.Service) *did.Service {
+	serviceConfig := config.DIDServiceConfig{BaseServiceConfig: &config.BaseServiceConfig{Name: "did"}, Methods: []string{"key"}}
+	// create a did service
+	didService, err := did.NewDIDService(serviceConfig, db, keyStore)
+	require.NoError(t, err)
+	require.NotEmpty(t, didService)
+	return didService
+}
+
 func testCredentialService(t *testing.T, db *storage.BoltDB, keyStore *keystore.Service) *credential.Service {
 	serviceConfig := config.CredentialServiceConfig{BaseServiceConfig: &config.BaseServiceConfig{Name: "credential"}}
 	// create a credential service
diff --git a/pkg/server/server_credential_test.go b/pkg/server/server_credential_test.go
new file mode 100644
index 0000000..eb65387
--- /dev/null
+++ b/pkg/server/server_credential_test.go
@@ -0,0 +1,428 @@
+package server
+
+import (
+	"fmt"
+	"net/http"
+	"net/http/httptest"
+	"os"
+	"testing"
+	"time"
+
+	credsdk "github.com/TBD54566975/ssi-sdk/credential"
+	"github.com/TBD54566975/ssi-sdk/credential/signing"
+	"github.com/TBD54566975/ssi-sdk/crypto"
+	"github.com/goccy/go-json"
+	"github.com/stretchr/testify/assert"
+
+	"github.com/tbd54566975/ssi-service/pkg/server/router"
+	"github.com/tbd54566975/ssi-service/pkg/service/did"
+	"github.com/tbd54566975/ssi-service/pkg/storage"
+)
+
+func TestCredentialAPI(t *testing.T) {
+	t.Run("Test Create Credential", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		keyStoreService := testKeyStoreService(tt, bolt)
+		credService := testCredentialRouter(tt, bolt, keyStoreService)
+		didService := testDIDService(tt, bolt, keyStoreService)
+
+		issuerDID, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
+			Method:  did.KeyMethod,
+			KeyType: crypto.Ed25519,
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, issuerDID)
+
+		// missing required field: data
+		badCredRequest := router.CreateCredentialRequest{
+			Issuer:  issuerDID.DID.ID,
+			Subject: "did:abc:456",
+			Expiry:  time.Now().Add(24 * time.Hour).Format(time.RFC3339),
+		}
+		badRequestValue := newRequestValue(tt, badCredRequest)
+		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/credentials", badRequestValue)
+		w := httptest.NewRecorder()
+
+		err = credService.CreateCredential(newRequestContext(), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), "invalid create credential request")
+
+		// reset the http recorder
+		w.Flush()
+
+		// missing known issuer request
+		missingIssuerRequest := router.CreateCredentialRequest{
+			Issuer:  "did:abc:123",
+			Subject: "did:abc:456",
+			Data: map[string]interface{}{
+				"firstName": "Jack",
+				"lastName":  "Dorsey",
+			},
+			Expiry: time.Now().Add(24 * time.Hour).Format(time.RFC3339),
+		}
+		missingIssuerRequestValue := newRequestValue(tt, missingIssuerRequest)
+		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/credentials", missingIssuerRequestValue)
+		err = credService.CreateCredential(newRequestContext(), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), "could not get key for signing credential with key<did:abc:123>")
+
+		// reset the http recorder
+		w.Flush()
+
+		// good request
+		createCredRequest := router.CreateCredentialRequest{
+			Issuer:  issuerDID.DID.ID,
+			Subject: "did:abc:456",
+			Data: map[string]interface{}{
+				"firstName": "Jack",
+				"lastName":  "Dorsey",
+			},
+			Expiry: time.Now().Add(24 * time.Hour).Format(time.RFC3339),
+		}
+		requestValue := newRequestValue(tt, createCredRequest)
+		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/credentials", requestValue)
+		err = credService.CreateCredential(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		var resp router.CreateCredentialResponse
+		err = json.NewDecoder(w.Body).Decode(&resp)
+		assert.NoError(tt, err)
+
+		assert.NotEmpty(tt, resp.CredentialJWT)
+		parsedCred, err := signing.ParseVerifiableCredentialFromJWT(*resp.CredentialJWT)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, parsedCred)
+		assert.Equal(tt, parsedCred.Issuer, issuerDID.DID.ID)
+	})
+
+	t.Run("Test Get Credential By ID", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		keyStoreService := testKeyStoreService(tt, bolt)
+		credService := testCredentialRouter(tt, bolt, keyStoreService)
+		didService := testDIDService(tt, bolt, keyStoreService)
+
+		w := httptest.NewRecorder()
+
+		// get a cred that doesn't exit
+		req := httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/credentials/bad", nil)
+		err = credService.GetCredential(newRequestContext(), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), "cannot get credential without ID parameter")
+
+		// reset recorder between calls
+		w.Flush()
+
+		// get a cred with an invalid id parameter
+		req = httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/credentials/bad", nil)
+		err = credService.GetCredential(newRequestContextWithParams(map[string]string{"id": "bad"}), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), "could not get credential with id: bad")
+
+		// reset recorder between calls
+		w.Flush()
+
+		issuerDID, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
+			Method:  did.KeyMethod,
+			KeyType: crypto.Ed25519,
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, issuerDID)
+
+		createCredRequest := router.CreateCredentialRequest{
+			Issuer:  issuerDID.DID.ID,
+			Subject: "did:abc:456",
+			Data: map[string]interface{}{
+				"firstName": "Jack",
+				"lastName":  "Dorsey",
+			},
+			Expiry: time.Now().Add(24 * time.Hour).Format(time.RFC3339),
+		}
+		requestValue := newRequestValue(tt, createCredRequest)
+		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/credentials", requestValue)
+		err = credService.CreateCredential(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		// We expect a JWT credential
+		var resp router.CreateCredentialResponse
+		err = json.NewDecoder(w.Body).Decode(&resp)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, resp.Credential)
+		assert.NotEmpty(tt, resp.CredentialJWT)
+
+		// get credential by id
+		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/credentials/%s", resp.Credential.ID), nil)
+		err = credService.GetCredential(newRequestContextWithParams(map[string]string{"id": resp.Credential.ID}), w, req)
+		assert.NoError(tt, err)
+
+		var getCredResp router.GetCredentialResponse
+		err = json.NewDecoder(w.Body).Decode(&getCredResp)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, getCredResp)
+		assert.NotEmpty(tt, getCredResp.CredentialJWT)
+		assert.Equal(tt, resp.Credential.ID, getCredResp.ID)
+	})
+
+	t.Run("Test Get Credential By Schema", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		keyStoreService := testKeyStoreService(tt, bolt)
+		credService := testCredentialRouter(tt, bolt, keyStoreService)
+		didService := testDIDService(tt, bolt, keyStoreService)
+
+		w := httptest.NewRecorder()
+
+		issuerDID, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
+			Method:  did.KeyMethod,
+			KeyType: crypto.Ed25519,
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, issuerDID)
+
+		schemaID := "https://test-schema.com/name"
+		createCredRequest := router.CreateCredentialRequest{
+			Issuer:  issuerDID.DID.ID,
+			Subject: "did:abc:456",
+			Schema:  schemaID,
+			Data: map[string]interface{}{
+				"firstName": "Jack",
+				"lastName":  "Dorsey",
+			},
+			Expiry: time.Now().Add(24 * time.Hour).Format(time.RFC3339),
+		}
+		requestValue := newRequestValue(tt, createCredRequest)
+		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/credentials", requestValue)
+		err = credService.CreateCredential(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		var resp router.CreateCredentialResponse
+		err = json.NewDecoder(w.Body).Decode(&resp)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, resp.CredentialJWT)
+
+		w.Flush()
+
+		// get credential by schema
+		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/credential?schema=%s", schemaID), nil)
+		err = credService.GetCredentials(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		var getCredsResp router.GetCredentialsResponse
+		err = json.NewDecoder(w.Body).Decode(&getCredsResp)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, getCredsResp)
+		assert.Len(tt, getCredsResp.Credentials, 1)
+
+		assert.Equal(tt, resp.Credential.ID, getCredsResp.Credentials[0].ID)
+		assert.Equal(tt, resp.Credential.CredentialSchema.ID, getCredsResp.Credentials[0].Credential.CredentialSchema.ID)
+	})
+
+	t.Run("Test Get Credential By Issuer", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		keyStoreService := testKeyStoreService(tt, bolt)
+		credService := testCredentialRouter(tt, bolt, keyStoreService)
+		didService := testDIDService(tt, bolt, keyStoreService)
+
+		w := httptest.NewRecorder()
+
+		issuerDID, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
+			Method:  did.KeyMethod,
+			KeyType: crypto.Ed25519,
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, issuerDID)
+
+		schemaID := "https://test-schema.com/name"
+		createCredRequest := router.CreateCredentialRequest{
+			Issuer:  issuerDID.DID.ID,
+			Subject: "did:abc:456",
+			Schema:  schemaID,
+			Data: map[string]interface{}{
+				"firstName": "Jack",
+				"lastName":  "Dorsey",
+			},
+			Expiry: time.Now().Add(24 * time.Hour).Format(time.RFC3339),
+		}
+		requestValue := newRequestValue(tt, createCredRequest)
+		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/credentials", requestValue)
+		err = credService.CreateCredential(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		var resp router.CreateCredentialResponse
+		err = json.NewDecoder(w.Body).Decode(&resp)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, resp.CredentialJWT)
+
+		w.Flush()
+
+		// get credential by issuer id
+		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/credential?issuer=%s", issuerDID.DID.ID), nil)
+		err = credService.GetCredentials(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		var getCredsResp router.GetCredentialsResponse
+		err = json.NewDecoder(w.Body).Decode(&getCredsResp)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, getCredsResp)
+
+		assert.Len(tt, getCredsResp.Credentials, 1)
+		assert.Equal(tt, resp.Credential.ID, getCredsResp.Credentials[0].ID)
+		assert.Equal(tt, resp.Credential.CredentialSchema.ID, getCredsResp.Credentials[0].Credential.CredentialSchema.ID)
+	})
+
+	t.Run("Test Get Credential By Subject", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		keyStoreService := testKeyStoreService(tt, bolt)
+		credService := testCredentialRouter(tt, bolt, keyStoreService)
+		didService := testDIDService(tt, bolt, keyStoreService)
+
+		w := httptest.NewRecorder()
+
+		issuerDID, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
+			Method:  did.KeyMethod,
+			KeyType: crypto.Ed25519,
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, issuerDID)
+
+		schemaID := "https://test-schema.com/name"
+		subjectID := "did:abc:456"
+		createCredRequest := router.CreateCredentialRequest{
+			Issuer:  issuerDID.DID.ID,
+			Subject: subjectID,
+			Schema:  schemaID,
+			Data: map[string]interface{}{
+				"firstName": "Jack",
+				"lastName":  "Dorsey",
+			},
+			Expiry: time.Now().Add(24 * time.Hour).Format(time.RFC3339),
+		}
+		requestValue := newRequestValue(tt, createCredRequest)
+		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/credentials", requestValue)
+		err = credService.CreateCredential(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		var resp router.CreateCredentialResponse
+		err = json.NewDecoder(w.Body).Decode(&resp)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, resp.CredentialJWT)
+
+		w.Flush()
+
+		// get credential by subject id
+		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/credential?subject=%s", subjectID), nil)
+		err = credService.GetCredentials(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		var getCredsResp router.GetCredentialsResponse
+		err = json.NewDecoder(w.Body).Decode(&getCredsResp)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, getCredsResp)
+
+		assert.Len(tt, getCredsResp.Credentials, 1)
+		assert.Equal(tt, resp.Credential.ID, getCredsResp.Credentials[0].ID)
+		assert.Equal(tt, resp.Credential.CredentialSubject[credsdk.VerifiableCredentialIDProperty], getCredsResp.Credentials[0].Credential.CredentialSubject[credsdk.VerifiableCredentialIDProperty])
+	})
+
+	t.Run("Test Delete Credential", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		keyStoreService := testKeyStoreService(tt, bolt)
+		credService := testCredentialRouter(tt, bolt, keyStoreService)
+		didService := testDIDService(tt, bolt, keyStoreService)
+
+		issuerDID, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
+			Method:  did.KeyMethod,
+			KeyType: crypto.Ed25519,
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, issuerDID)
+
+		createCredRequest := router.CreateCredentialRequest{
+			Issuer:  issuerDID.DID.ID,
+			Subject: "did:abc:456",
+			Data: map[string]interface{}{
+				"firstName": "Jack",
+				"lastName":  "Dorsey",
+			},
+			Expiry: time.Now().Add(24 * time.Hour).Format(time.RFC3339),
+		}
+		requestValue := newRequestValue(tt, createCredRequest)
+		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/credentials", requestValue)
+		w := httptest.NewRecorder()
+		err = credService.CreateCredential(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		var resp router.CreateCredentialResponse
+		err = json.NewDecoder(w.Body).Decode(&resp)
+		assert.NoError(tt, err)
+
+		w.Flush()
+
+		// get credential by id
+		credID := resp.Credential.ID
+		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/credentials/%s", credID), nil)
+		err = credService.GetCredential(newRequestContextWithParams(map[string]string{"id": credID}), w, req)
+		assert.NoError(tt, err)
+
+		var getCredResp router.GetCredentialResponse
+		err = json.NewDecoder(w.Body).Decode(&getCredResp)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, getCredResp)
+		assert.Equal(tt, credID, getCredResp.Credential.ID)
+
+		w.Flush()
+
+		// delete it
+		req = httptest.NewRequest(http.MethodDelete, fmt.Sprintf("https://ssi-service.com/v1/credentials/%s", credID), nil)
+		err = credService.DeleteCredential(newRequestContextWithParams(map[string]string{"id": credID}), w, req)
+		assert.NoError(tt, err)
+
+		w.Flush()
+
+		// get it back
+		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/credentials/%s", credID), nil)
+		err = credService.GetCredential(newRequestContextWithParams(map[string]string{"id": credID}), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), fmt.Sprintf("could not get credential with id: %s", credID))
+	})
+}
diff --git a/pkg/server/server_did_test.go b/pkg/server/server_did_test.go
new file mode 100644
index 0000000..f5bf3b5
--- /dev/null
+++ b/pkg/server/server_did_test.go
@@ -0,0 +1,262 @@
+package server
+
+import (
+	"fmt"
+	"net/http"
+	"net/http/httptest"
+	"os"
+	"testing"
+
+	"github.com/TBD54566975/ssi-sdk/crypto"
+	"github.com/goccy/go-json"
+	"github.com/stretchr/testify/assert"
+
+	"github.com/tbd54566975/ssi-service/pkg/server/router"
+	"github.com/tbd54566975/ssi-service/pkg/service/did"
+	"github.com/tbd54566975/ssi-service/pkg/storage"
+)
+
+func TestDIDAPI(t *testing.T) {
+	t.Run("Test Get DID Methods", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		_, keyStoreService := testKeyStore(tt, bolt)
+		didService := testDIDRouter(tt, bolt, keyStoreService)
+
+		// get DID methods
+		req := httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/dids", nil)
+		w := httptest.NewRecorder()
+
+		err = didService.GetDIDMethods(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+		assert.Equal(tt, http.StatusOK, w.Result().StatusCode)
+
+		var resp router.GetDIDMethodsResponse
+		err = json.NewDecoder(w.Body).Decode(&resp)
+		assert.NoError(tt, err)
+
+		assert.Len(tt, resp.DIDMethods, 1)
+		assert.Equal(tt, resp.DIDMethods[0], did.KeyMethod)
+	})
+
+	t.Run("Test Create DID By Method: Key", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		_, keyStoreService := testKeyStore(tt, bolt)
+		didService := testDIDRouter(tt, bolt, keyStoreService)
+
+		// create DID by method - key - missing body
+		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/dids/key", nil)
+		w := httptest.NewRecorder()
+		params := map[string]string{
+			"method": "key",
+		}
+
+		err = didService.CreateDIDByMethod(newRequestContextWithParams(params), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), "invalid create DID request")
+
+		// reset recorder between calls
+		w.Flush()
+
+		// with body, bad key type
+		createDIDRequest := router.CreateDIDByMethodRequest{KeyType: "bad"}
+		requestReader := newRequestValue(tt, createDIDRequest)
+		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/dids/key", requestReader)
+
+		err = didService.CreateDIDByMethod(newRequestContextWithParams(params), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), "could not create DID for method<key> with key type: bad")
+
+		// reset recorder between calls
+		w.Flush()
+
+		// with body, good key type
+		createDIDRequest = router.CreateDIDByMethodRequest{KeyType: crypto.Ed25519}
+		requestReader = newRequestValue(tt, createDIDRequest)
+		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/dids/key", requestReader)
+
+		err = didService.CreateDIDByMethod(newRequestContextWithParams(params), w, req)
+		assert.NoError(tt, err)
+
+		var resp router.CreateDIDByMethodResponse
+		err = json.NewDecoder(w.Body).Decode(&resp)
+		assert.NoError(tt, err)
+
+		assert.Contains(tt, resp.DID.ID, did.KeyMethod)
+	})
+
+	t.Run("Test Get DID By Method", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		_, keyStore := testKeyStore(tt, bolt)
+		didService := testDIDRouter(tt, bolt, keyStore)
+
+		// get DID by method
+		req := httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/dids/bad/worse", nil)
+		w := httptest.NewRecorder()
+
+		// bad params
+		badParams := map[string]string{
+			"method": "bad",
+			"id":     "worse",
+		}
+		err = didService.GetDIDByMethod(newRequestContextWithParams(badParams), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), "could not get DID for method<bad>")
+
+		// reset recorder between calls
+		w.Flush()
+
+		// good method, bad id
+		badParams1 := map[string]string{
+			"method": "key",
+			"id":     "worse",
+		}
+		err = didService.GetDIDByMethod(newRequestContextWithParams(badParams1), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), "could not get DID for method<key> with id: worse")
+
+		// reset recorder between calls
+		w.Flush()
+		// store a DID
+		createDIDRequest := router.CreateDIDByMethodRequest{KeyType: crypto.Ed25519}
+		requestReader := newRequestValue(tt, createDIDRequest)
+		params := map[string]string{"method": "key"}
+		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/dids/key", requestReader)
+
+		err = didService.CreateDIDByMethod(newRequestContextWithParams(params), w, req)
+		assert.NoError(tt, err)
+
+		var createdDID router.CreateDIDByMethodResponse
+		err = json.NewDecoder(w.Body).Decode(&createdDID)
+		assert.NoError(tt, err)
+
+		// reset recorder between calls
+		w.Flush()
+
+		// get it back
+		createdID := createdDID.DID.ID
+		getDIDPath := fmt.Sprintf("https://ssi-service.com/v1/dids/key/%s", createdID)
+		req = httptest.NewRequest(http.MethodGet, getDIDPath, nil)
+
+		// good params
+		goodParams := map[string]string{
+			"method": "key",
+			"id":     createdID,
+		}
+		err = didService.GetDIDByMethod(newRequestContextWithParams(goodParams), w, req)
+		assert.NoError(tt, err)
+
+		var resp router.GetDIDByMethodResponse
+		err = json.NewDecoder(w.Body).Decode(&resp)
+		assert.NoError(tt, err)
+		assert.Equal(tt, createdID, resp.DID.ID)
+	})
+
+	t.Run("Test Get DIDs By Method", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		_, keyStore := testKeyStore(tt, bolt)
+		didService := testDIDRouter(tt, bolt, keyStore)
+
+		// get DIDs by method
+		req := httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/dids/bad", nil)
+		w := httptest.NewRecorder()
+
+		// bad params
+		badParams := map[string]string{
+			"method": "bad",
+		}
+		err = didService.GetDIDsByMethod(newRequestContextWithParams(badParams), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), "could not get DIDs for method: bad")
+
+		// good method
+		goodParams := map[string]string{
+			"method": "key",
+		}
+		err = didService.GetDIDsByMethod(newRequestContextWithParams(goodParams), w, req)
+		assert.NoError(tt, err)
+		var gotDIDs router.GetDIDByMethodResponse
+		err = json.NewDecoder(w.Body).Decode(&gotDIDs)
+		assert.NoError(tt, err)
+		assert.Empty(tt, gotDIDs)
+
+		// reset recorder between calls
+		w.Flush()
+
+		// store two DIDs
+		createDIDRequest := router.CreateDIDByMethodRequest{KeyType: crypto.Ed25519}
+		requestReader := newRequestValue(tt, createDIDRequest)
+		params := map[string]string{"method": "key"}
+		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/dids/key", requestReader)
+
+		err = didService.CreateDIDByMethod(newRequestContextWithParams(params), w, req)
+		assert.NoError(tt, err)
+
+		var createdDID router.CreateDIDByMethodResponse
+		err = json.NewDecoder(w.Body).Decode(&createdDID)
+		assert.NoError(tt, err)
+
+		// reset recorder between calls
+		w.Flush()
+
+		requestReader = newRequestValue(tt, createDIDRequest)
+		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/dids/key", requestReader)
+
+		err = didService.CreateDIDByMethod(newRequestContextWithParams(params), w, req)
+		assert.NoError(tt, err)
+
+		var createdDID2 router.CreateDIDByMethodResponse
+		err = json.NewDecoder(w.Body).Decode(&createdDID2)
+		assert.NoError(tt, err)
+
+		// reset recorder between calls
+		w.Flush()
+
+		// get all dids for method
+
+		req = httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/dids/key", requestReader)
+		err = didService.GetDIDsByMethod(newRequestContextWithParams(params), w, req)
+		assert.NoError(tt, err)
+
+		var gotDIDsResponse router.GetDIDsByMethodResponse
+		err = json.NewDecoder(w.Body).Decode(&gotDIDsResponse)
+		assert.NoError(tt, err)
+
+		knownDIDs := map[string]bool{createdDID.DID.ID: true, createdDID2.DID.ID: true}
+		for _, did := range gotDIDsResponse.DIDs {
+			if _, ok := knownDIDs[did.ID]; !ok {
+				tt.Error("got unknown DID")
+			} else {
+				delete(knownDIDs, did.ID)
+			}
+		}
+		assert.Len(tt, knownDIDs, 0)
+	})
+}
diff --git a/pkg/server/server_dwn_test.go b/pkg/server/server_dwn_test.go
new file mode 100644
index 0000000..7b7cfde
--- /dev/null
+++ b/pkg/server/server_dwn_test.go
@@ -0,0 +1,66 @@
+package server
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"os"
+	"testing"
+
+	"github.com/TBD54566975/ssi-sdk/crypto"
+	"github.com/goccy/go-json"
+	"github.com/stretchr/testify/assert"
+
+	"github.com/tbd54566975/ssi-service/pkg/server/router"
+	"github.com/tbd54566975/ssi-service/pkg/service/did"
+	"github.com/tbd54566975/ssi-service/pkg/storage"
+)
+
+func TestDWNAPI(t *testing.T) {
+	t.Run("Test DWN Publish Manifest", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		keyStoreService := testKeyStoreService(tt, bolt)
+		credentialService := testCredentialService(tt, bolt, keyStoreService)
+		didService := testDIDService(tt, bolt, keyStoreService)
+		manifestRouter, manifestService := testManifest(tt, bolt, keyStoreService, credentialService)
+		dwnService := testDWNRouter(tt, bolt, keyStoreService, manifestService)
+
+		w := httptest.NewRecorder()
+
+		// create an issuer
+		issuerDIDDoc, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
+			Method:  did.KeyMethod,
+			KeyType: crypto.Ed25519,
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, issuerDIDDoc)
+
+		// good request
+		createManifestRequest := getValidManifestRequest(issuerDIDDoc.DID.ID)
+
+		requestValue := newRequestValue(tt, createManifestRequest)
+		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests", requestValue)
+		err = manifestRouter.CreateManifest(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		var resp router.CreateManifestResponse
+		err = json.NewDecoder(w.Body).Decode(&resp)
+		assert.NoError(tt, err)
+
+		w = httptest.NewRecorder()
+
+		dwnRequest := router.PublishManifestRequest{ManifestID: "WA-DL-CLASS-A"}
+		dwnRequestValue := newRequestValue(tt, dwnRequest)
+		dwnReq := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/dwn/manifests", dwnRequestValue)
+		err = dwnService.PublishManifest(newRequestContext(), w, dwnReq)
+
+		assert.Error(tt, err)
+		assert.ErrorContains(tt, err, "unsupported protocol scheme")
+	})
+}
diff --git a/pkg/server/server_keystore_test.go b/pkg/server/server_keystore_test.go
new file mode 100644
index 0000000..bfb5d64
--- /dev/null
+++ b/pkg/server/server_keystore_test.go
@@ -0,0 +1,116 @@
+package server
+
+import (
+	"fmt"
+	"net/http"
+	"net/http/httptest"
+	"os"
+	"testing"
+
+	"github.com/TBD54566975/ssi-sdk/crypto"
+	"github.com/goccy/go-json"
+	"github.com/mr-tron/base58"
+	"github.com/stretchr/testify/assert"
+
+	"github.com/tbd54566975/ssi-service/pkg/server/router"
+	"github.com/tbd54566975/ssi-service/pkg/storage"
+)
+
+func TestKeyStoreAPI(t *testing.T) {
+	t.Run("Test Store Key", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		keyStoreRouter, _ := testKeyStore(tt, bolt)
+		w := httptest.NewRecorder()
+
+		// bad key type
+		badKeyStoreRequest := router.StoreKeyRequest{
+			ID:               "test-kid",
+			Type:             "bad",
+			Controller:       "me",
+			Base58PrivateKey: "bad",
+		}
+		badRequestValue := newRequestValue(tt, badKeyStoreRequest)
+		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/keys", badRequestValue)
+		err = keyStoreRouter.StoreKey(newRequestContext(), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), "unsupported key type: bad")
+
+		// reset the http recorder
+		w.Flush()
+
+		// store a valid key
+		_, privKey, err := crypto.GenerateKeyByKeyType(crypto.Ed25519)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, privKey)
+
+		privKeyBytes, err := crypto.PrivKeyToBytes(privKey)
+		assert.NoError(tt, err)
+
+		// good request
+		storeKeyRequest := router.StoreKeyRequest{
+			ID:               "did:test:me#key-1",
+			Type:             crypto.Ed25519,
+			Controller:       "did:test:me",
+			Base58PrivateKey: base58.Encode(privKeyBytes),
+		}
+		requestValue := newRequestValue(tt, storeKeyRequest)
+		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/keys", requestValue)
+		err = keyStoreRouter.StoreKey(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+	})
+
+	t.Run("Test Get Key Details", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		keyStoreService, _ := testKeyStore(tt, bolt)
+		w := httptest.NewRecorder()
+
+		// store a valid key
+		_, privKey, err := crypto.GenerateKeyByKeyType(crypto.Ed25519)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, privKey)
+
+		privKeyBytes, err := crypto.PrivKeyToBytes(privKey)
+		assert.NoError(tt, err)
+
+		// good request
+		keyID := "did:test:me#key-2"
+		controller := "did:test:me"
+		storeKeyRequest := router.StoreKeyRequest{
+			ID:               keyID,
+			Type:             crypto.Ed25519,
+			Controller:       controller,
+			Base58PrivateKey: base58.Encode(privKeyBytes),
+		}
+		requestValue := newRequestValue(tt, storeKeyRequest)
+		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/keys", requestValue)
+		err = keyStoreService.StoreKey(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		// get it back
+		getRecorder := httptest.NewRecorder()
+		getReq := httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/keys/%s", keyID), nil)
+		err = keyStoreService.GetKeyDetails(newRequestContextWithParams(map[string]string{"id": keyID}), getRecorder, getReq)
+		assert.NoError(tt, err)
+
+		var resp router.GetKeyDetailsResponse
+		err = json.NewDecoder(getRecorder.Body).Decode(&resp)
+		assert.NoError(tt, err)
+		assert.Equal(tt, keyID, resp.ID)
+		assert.Equal(tt, controller, resp.Controller)
+		assert.Equal(tt, crypto.Ed25519, resp.Type)
+	})
+}
diff --git a/pkg/server/server_manifest_test.go b/pkg/server/server_manifest_test.go
new file mode 100644
index 0000000..c85387c
--- /dev/null
+++ b/pkg/server/server_manifest_test.go
@@ -0,0 +1,514 @@
+package server
+
+import (
+	"fmt"
+	"net/http"
+	"net/http/httptest"
+	"os"
+	"testing"
+
+	"github.com/TBD54566975/ssi-sdk/crypto"
+	"github.com/goccy/go-json"
+	"github.com/stretchr/testify/assert"
+
+	"github.com/tbd54566975/ssi-service/pkg/server/router"
+	"github.com/tbd54566975/ssi-service/pkg/service/did"
+	"github.com/tbd54566975/ssi-service/pkg/storage"
+)
+
+func TestManifestAPI(t *testing.T) {
+	t.Run("Test Create Manifest", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		keyStoreService := testKeyStoreService(tt, bolt)
+		credentialService := testCredentialService(tt, bolt, keyStoreService)
+		didService := testDIDService(tt, bolt, keyStoreService)
+		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
+
+		// missing required field: Manifest
+		badManifestRequest := router.CreateManifestRequest{}
+
+		badRequestValue := newRequestValue(tt, badManifestRequest)
+		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests", badRequestValue)
+		w := httptest.NewRecorder()
+
+		err = manifestRouter.CreateManifest(newRequestContext(), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), "invalid create manifest request")
+
+		// reset the http recorder
+		w.Flush()
+
+		// create an issuer
+		issuerDIDDoc, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
+			Method:  did.KeyMethod,
+			KeyType: crypto.Ed25519,
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, issuerDIDDoc)
+
+		issuerDID := issuerDIDDoc.DID.ID
+
+		// good request
+		createManifestRequest := getValidManifestRequest(issuerDID)
+
+		requestValue := newRequestValue(tt, createManifestRequest)
+		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests", requestValue)
+		err = manifestRouter.CreateManifest(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		var resp router.CreateManifestResponse
+		err = json.NewDecoder(w.Body).Decode(&resp)
+		assert.NoError(tt, err)
+
+		assert.NotEmpty(tt, resp.Manifest)
+		assert.Equal(tt, resp.Manifest.Issuer.ID, issuerDID)
+	})
+
+	t.Run("Test Get Manifest By ID", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		keyStoreService := testKeyStoreService(tt, bolt)
+		credentialService := testCredentialService(tt, bolt, keyStoreService)
+		didService := testDIDService(tt, bolt, keyStoreService)
+		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
+
+		w := httptest.NewRecorder()
+
+		// get a manifest that doesn't exit
+		req := httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/manifests/bad", nil)
+		err = manifestRouter.GetManifest(newRequestContext(), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), "cannot get manifest without ID parameter")
+
+		// reset recorder between calls
+		w.Flush()
+
+		// get a manifest with an invalid id parameter
+		req = httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/manifests/bad", nil)
+		err = manifestRouter.GetManifest(newRequestContextWithParams(map[string]string{"id": "bad"}), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), "could not get manifest with id: bad")
+
+		// reset recorder between calls
+		w.Flush()
+
+		// create an issuer
+		issuerDIDDoc, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
+			Method:  did.KeyMethod,
+			KeyType: crypto.Ed25519,
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, issuerDIDDoc)
+
+		// good request
+		createManifestRequest := getValidManifestRequest(issuerDIDDoc.DID.ID)
+
+		requestValue := newRequestValue(tt, createManifestRequest)
+		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests", requestValue)
+		err = manifestRouter.CreateManifest(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		var resp router.CreateManifestResponse
+		err = json.NewDecoder(w.Body).Decode(&resp)
+		assert.NoError(tt, err)
+
+		// get manifest by id
+		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/manifests/%s", resp.Manifest.ID), nil)
+		err = manifestRouter.GetManifest(newRequestContextWithParams(map[string]string{"id": resp.Manifest.ID}), w, req)
+		assert.NoError(tt, err)
+
+		var getManifestResp router.GetManifestResponse
+		err = json.NewDecoder(w.Body).Decode(&getManifestResp)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, getManifestResp)
+		assert.Equal(tt, resp.Manifest.ID, getManifestResp.ID)
+	})
+
+	t.Run("Test Get Manifests", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		keyStoreService := testKeyStoreService(tt, bolt)
+		credentialService := testCredentialService(tt, bolt, keyStoreService)
+		didService := testDIDService(tt, bolt, keyStoreService)
+		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
+
+		w := httptest.NewRecorder()
+
+		// create an issuer
+		issuerDIDDoc, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
+			Method:  did.KeyMethod,
+			KeyType: crypto.Ed25519,
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, issuerDIDDoc)
+
+		// good request
+		createManifestRequest := getValidManifestRequest(issuerDIDDoc.DID.ID)
+
+		requestValue := newRequestValue(tt, createManifestRequest)
+		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests", requestValue)
+		err = manifestRouter.CreateManifest(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		var resp router.CreateManifestResponse
+		err = json.NewDecoder(w.Body).Decode(&resp)
+		assert.NoError(tt, err)
+
+		// get manifest by id
+		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/manifests"), nil)
+		err = manifestRouter.GetManifests(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		var getManifestsResp router.GetManifestsResponse
+		err = json.NewDecoder(w.Body).Decode(&getManifestsResp)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, getManifestsResp)
+		assert.Len(tt, getManifestsResp.Manifests, 1)
+		assert.Equal(tt, resp.Manifest.ID, getManifestsResp.Manifests[0].ID)
+	})
+
+	t.Run("Test Delete Manifest", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		keyStoreService := testKeyStoreService(tt, bolt)
+		credentialService := testCredentialService(tt, bolt, keyStoreService)
+		didService := testDIDService(tt, bolt, keyStoreService)
+		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
+
+		// create an issuer
+		issuerDIDDoc, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
+			Method:  did.KeyMethod,
+			KeyType: crypto.Ed25519,
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, issuerDIDDoc)
+
+		// good request
+
+		createManifestRequest := getValidManifestRequest(issuerDIDDoc.DID.ID)
+
+		requestValue := newRequestValue(tt, createManifestRequest)
+		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests", requestValue)
+		w := httptest.NewRecorder()
+		err = manifestRouter.CreateManifest(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		var resp router.CreateManifestResponse
+		err = json.NewDecoder(w.Body).Decode(&resp)
+		assert.NoError(tt, err)
+
+		w.Flush()
+
+		// get credential by id
+		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/manifests/%s", resp.Manifest.ID), nil)
+		err = manifestRouter.GetManifest(newRequestContextWithParams(map[string]string{"id": resp.Manifest.ID}), w, req)
+		assert.NoError(tt, err)
+
+		var getManifestResp router.GetCredentialResponse
+		err = json.NewDecoder(w.Body).Decode(&getManifestResp)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, getManifestResp)
+		assert.Equal(tt, resp.Manifest.ID, getManifestResp.ID)
+
+		w.Flush()
+
+		// delete it
+		req = httptest.NewRequest(http.MethodDelete, fmt.Sprintf("https://ssi-service.com/v1/manifests/%s", resp.Manifest.ID), nil)
+		err = manifestRouter.DeleteManifest(newRequestContextWithParams(map[string]string{"id": resp.Manifest.ID}), w, req)
+		assert.NoError(tt, err)
+
+		w.Flush()
+
+		// get it back
+		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/manifests/%s", resp.Manifest.ID), nil)
+		err = manifestRouter.GetManifest(newRequestContextWithParams(map[string]string{"id": resp.Manifest.ID}), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), fmt.Sprintf("could not get manifest with id: %s", resp.Manifest.ID))
+	})
+
+	t.Run("Test Submit Application", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		keyStoreService := testKeyStoreService(tt, bolt)
+		credentialService := testCredentialService(tt, bolt, keyStoreService)
+		didService := testDIDService(tt, bolt, keyStoreService)
+
+		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
+
+		// missing required field: Application
+		badManifestRequest := router.SubmitApplicationRequest{
+			ApplicantDID: "did:example:abcd",
+		}
+
+		badRequestValue := newRequestValue(tt, badManifestRequest)
+		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests/applications", badRequestValue)
+		w := httptest.NewRecorder()
+
+		err = manifestRouter.SubmitApplication(newRequestContext(), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), "invalid submit application request")
+
+		// reset the http recorder
+		w.Flush()
+
+		// create an issuer
+		issuerDIDDoc, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
+			Method:  did.KeyMethod,
+			KeyType: crypto.Ed25519,
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, issuerDIDDoc)
+
+		issuerDID := issuerDIDDoc.DID.ID
+		applicantDID := "did:example:abcd"
+
+		// good request
+		createManifestRequest := getValidManifestRequest(issuerDID)
+
+		requestValue := newRequestValue(tt, createManifestRequest)
+		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests", requestValue)
+		err = manifestRouter.CreateManifest(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		var resp router.CreateManifestResponse
+		err = json.NewDecoder(w.Body).Decode(&resp)
+		assert.NoError(tt, err)
+
+		assert.NotEmpty(tt, resp.Manifest)
+		assert.Equal(tt, resp.Manifest.Issuer.ID, issuerDID)
+
+		// good application request
+		createApplicationRequest := getValidApplicationRequest(applicantDID, resp.Manifest.ID, resp.Manifest.PresentationDefinition.InputDescriptors[0].ID)
+
+		applicationRequestValue := newRequestValue(tt, createApplicationRequest)
+		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests/applications", applicationRequestValue)
+		err = manifestRouter.SubmitApplication(newRequestContext(), w, req)
+
+		var appResp router.SubmitApplicationResponse
+		err = json.NewDecoder(w.Body).Decode(&appResp)
+		assert.NoError(tt, err)
+
+		assert.NotEmpty(tt, appResp.Response)
+		assert.Equal(tt, appResp.Response.ManifestID, resp.Manifest.ID)
+
+	})
+
+	t.Run("Test Get Application By ID and Get Applications", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		keyStoreService := testKeyStoreService(tt, bolt)
+		credentialService := testCredentialService(tt, bolt, keyStoreService)
+		didService := testDIDService(tt, bolt, keyStoreService)
+		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
+		w := httptest.NewRecorder()
+
+		// get a application that doesn't exit
+		req := httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/manifests/applications/bad", nil)
+		err = manifestRouter.GetApplication(newRequestContext(), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), "cannot get application without ID parameter")
+
+		// reset recorder between calls
+		w.Flush()
+
+		// create an issuer
+		issuerDIDDoc, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
+			Method:  did.KeyMethod,
+			KeyType: crypto.Ed25519,
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, issuerDIDDoc)
+
+		issuerDID := issuerDIDDoc.DID.ID
+		applicantDID := "did:example:abcd"
+
+		// good manifest request
+		createManifestRequest := getValidManifestRequest(issuerDID)
+
+		requestValue := newRequestValue(tt, createManifestRequest)
+		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests", requestValue)
+		err = manifestRouter.CreateManifest(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		var resp router.CreateManifestResponse
+		err = json.NewDecoder(w.Body).Decode(&resp)
+		assert.NoError(tt, err)
+
+		// good application request
+		createApplicationRequest := getValidApplicationRequest(applicantDID, resp.Manifest.ID, resp.Manifest.PresentationDefinition.InputDescriptors[0].ID)
+
+		applicationRequestValue := newRequestValue(tt, createApplicationRequest)
+		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests/applications", applicationRequestValue)
+		err = manifestRouter.SubmitApplication(newRequestContext(), w, req)
+
+		var appResp router.SubmitApplicationResponse
+		err = json.NewDecoder(w.Body).Decode(&appResp)
+		assert.NoError(tt, err)
+
+		// get response by id
+		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/manifests/responses/%s", appResp.Response.ID), nil)
+		err = manifestRouter.GetResponse(newRequestContextWithParams(map[string]string{"id": appResp.Response.ID}), w, req)
+		assert.NoError(tt, err)
+
+		var getResponseResponse router.GetResponseResponse
+		err = json.NewDecoder(w.Body).Decode(&getResponseResponse)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, getResponseResponse)
+		assert.Equal(tt, appResp.Response.ID, getResponseResponse.Response.ID)
+
+		// get all responses
+		req = httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/manifests/responses", nil)
+		err = manifestRouter.GetResponses(newRequestContext(), w, req)
+
+		var getResponsesResp router.GetResponsesResponse
+		err = json.NewDecoder(w.Body).Decode(&getResponsesResp)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, getResponsesResp)
+
+		assert.Len(tt, getResponsesResp.Responses, 1)
+
+		// get all applications
+		req = httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/manifests/applications", applicationRequestValue)
+		err = manifestRouter.GetApplications(newRequestContext(), w, req)
+
+		var getApplicationsResp router.GetApplicationsResponse
+		err = json.NewDecoder(w.Body).Decode(&getApplicationsResp)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, getApplicationsResp)
+
+		assert.Len(tt, getApplicationsResp.Applications, 1)
+
+		// get application by id
+		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/manifests/applications/%s", getApplicationsResp.Applications[0].ID), nil)
+		err = manifestRouter.GetApplication(newRequestContextWithParams(map[string]string{"id": getApplicationsResp.Applications[0].ID}), w, req)
+		assert.NoError(tt, err)
+
+		var getApplicationResponse router.GetApplicationResponse
+		err = json.NewDecoder(w.Body).Decode(&getApplicationResponse)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, getApplicationResponse)
+		assert.Equal(tt, getApplicationsResp.Applications[0].ID, getApplicationResponse.ID)
+	})
+
+	t.Run("Test Delete Application", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		keyStoreService := testKeyStoreService(tt, bolt)
+		credentialService := testCredentialService(tt, bolt, keyStoreService)
+		didService := testDIDService(tt, bolt, keyStoreService)
+		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
+
+		// create an issuer
+		issuerDIDDoc, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
+			Method:  did.KeyMethod,
+			KeyType: crypto.Ed25519,
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, issuerDIDDoc)
+
+		issuerDID := issuerDIDDoc.DID.ID
+		applicantDID := "did:example:abcd"
+
+		// good manifest request
+		createManifestRequest := getValidManifestRequest(issuerDID)
+
+		requestValue := newRequestValue(tt, createManifestRequest)
+		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests", requestValue)
+		w := httptest.NewRecorder()
+		err = manifestRouter.CreateManifest(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		var resp router.CreateManifestResponse
+		err = json.NewDecoder(w.Body).Decode(&resp)
+		assert.NoError(tt, err)
+
+		// good application request
+		createApplicationRequest := getValidApplicationRequest(applicantDID, resp.Manifest.ID, resp.Manifest.PresentationDefinition.InputDescriptors[0].ID)
+
+		applicationRequestValue := newRequestValue(tt, createApplicationRequest)
+		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests/applications", applicationRequestValue)
+		err = manifestRouter.SubmitApplication(newRequestContext(), w, req)
+
+		var appResp router.SubmitApplicationResponse
+		err = json.NewDecoder(w.Body).Decode(&appResp)
+		assert.NoError(tt, err)
+
+		// get all applications
+		req = httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/manifests/applications", applicationRequestValue)
+		err = manifestRouter.GetApplications(newRequestContext(), w, req)
+
+		var getApplicationsResp router.GetApplicationsResponse
+		err = json.NewDecoder(w.Body).Decode(&getApplicationsResp)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, getApplicationsResp)
+
+		assert.Len(tt, getApplicationsResp.Applications, 1)
+
+		// get the application
+		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/manifests/applications/%s", getApplicationsResp.Applications[0].ID), nil)
+		err = manifestRouter.GetApplication(newRequestContextWithParams(map[string]string{"id": getApplicationsResp.Applications[0].ID}), w, req)
+		assert.NoError(tt, err)
+
+		var getApplicationResp router.GetApplicationResponse
+		err = json.NewDecoder(w.Body).Decode(&getApplicationResp)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, getApplicationResp)
+		assert.Equal(tt, resp.Manifest.ID, getApplicationResp.Application.ManifestID)
+
+		// delete the application
+		req = httptest.NewRequest(http.MethodDelete, fmt.Sprintf("https://ssi-service.com/v1/manifests/applications/%s", getApplicationResp.Application.ID), nil)
+		err = manifestRouter.DeleteApplication(newRequestContextWithParams(map[string]string{"id": getApplicationResp.Application.ID}), w, req)
+		assert.NoError(tt, err)
+
+		w.Flush()
+
+		// get it back
+		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/manifests/applications/%s", appResp.Response.ID), nil)
+		err = manifestRouter.GetApplication(newRequestContextWithParams(map[string]string{"id": appResp.Response.ID}), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), fmt.Sprintf("could not get application with id: %s", appResp.Response.ID))
+	})
+}
diff --git a/pkg/server/server_schema_test.go b/pkg/server/server_schema_test.go
new file mode 100644
index 0000000..deb0d00
--- /dev/null
+++ b/pkg/server/server_schema_test.go
@@ -0,0 +1,160 @@
+package server
+
+import (
+	"fmt"
+	"net/http"
+	"net/http/httptest"
+	"os"
+	"testing"
+
+	"github.com/goccy/go-json"
+	"github.com/stretchr/testify/assert"
+
+	"github.com/tbd54566975/ssi-service/pkg/server/router"
+	"github.com/tbd54566975/ssi-service/pkg/storage"
+)
+
+func TestSchemaAPI(t *testing.T) {
+	t.Run("Test Create Schema", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		keyStoreService := testKeyStoreService(tt, bolt)
+		schemaService := testSchemaRouter(tt, bolt, keyStoreService)
+
+		simpleSchema := map[string]interface{}{
+			"type": "object",
+			"properties": map[string]interface{}{
+				"foo": map[string]interface{}{
+					"type": "string",
+				},
+			},
+			"required":             []interface{}{"foo"},
+			"additionalProperties": false,
+		}
+		badSchemaRequest := router.CreateSchemaRequest{Schema: simpleSchema}
+		schemaRequestValue := newRequestValue(tt, badSchemaRequest)
+		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/schemas", schemaRequestValue)
+		w := httptest.NewRecorder()
+
+		err = schemaService.CreateSchema(newRequestContext(), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), "invalid create schema request")
+
+		// reset the http recorder
+		w.Flush()
+
+		schemaRequest := router.CreateSchemaRequest{Author: "did:test", Name: "test schema", Schema: simpleSchema}
+		schemaRequestValue = newRequestValue(tt, schemaRequest)
+		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/schemas", schemaRequestValue)
+		err = schemaService.CreateSchema(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		var resp router.CreateSchemaResponse
+		err = json.NewDecoder(w.Body).Decode(&resp)
+		assert.NoError(tt, err)
+
+		assert.NotEmpty(tt, resp.ID)
+		assert.EqualValues(tt, schemaRequest.Schema, resp.Schema.Schema)
+	})
+
+	t.Run("Test Get Schemas", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		keyStoreService := testKeyStoreService(tt, bolt)
+		schemaService := testSchemaRouter(tt, bolt, keyStoreService)
+
+		// get schema that doesn't exist
+		w := httptest.NewRecorder()
+		req := httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/schemas/bad", nil)
+		err = schemaService.GetSchema(newRequestContext(), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), "cannot get schema without ID parameter")
+
+		// reset recorder between calls
+		w.Flush()
+
+		// get schema with invalid id
+		req = httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/schemas/bad", nil)
+		err = schemaService.GetSchema(newRequestContextWithParams(map[string]string{"id": "bad"}), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), "could not get schema with id: bad")
+
+		// reset recorder between calls
+		w.Flush()
+
+		// get all schemas - get none
+		req = httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/schemas", nil)
+		err = schemaService.GetSchemas(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+		var getSchemasResp router.GetSchemasResponse
+		err = json.NewDecoder(w.Body).Decode(&getSchemasResp)
+		assert.NoError(tt, err)
+		assert.Len(tt, getSchemasResp.Schemas, 0)
+
+		// reset recorder between calls
+		w.Flush()
+
+		// create a schema
+		simpleSchema := map[string]interface{}{
+			"type": "object",
+			"properties": map[string]interface{}{
+				"foo": map[string]interface{}{
+					"type": "string",
+				},
+			},
+			"required":             []interface{}{"foo"},
+			"additionalProperties": false,
+		}
+		schemaRequest := router.CreateSchemaRequest{Author: "did:test", Name: "test schema", Schema: simpleSchema}
+		schemaRequestValue := newRequestValue(tt, schemaRequest)
+		createReq := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/schemas", schemaRequestValue)
+
+		err = schemaService.CreateSchema(newRequestContext(), w, createReq)
+		assert.NoError(tt, err)
+
+		var createResp router.CreateSchemaResponse
+		err = json.NewDecoder(w.Body).Decode(&createResp)
+		assert.NoError(tt, err)
+
+		assert.NotEmpty(tt, createResp.ID)
+		assert.EqualValues(tt, schemaRequest.Schema, createResp.Schema.Schema)
+
+		// reset recorder between calls
+		w.Flush()
+
+		// get it back
+		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/schemas/%s", createResp.ID), nil)
+		err = schemaService.GetSchema(newRequestContextWithParams(map[string]string{"id": createResp.ID}), w, req)
+		assert.NoError(tt, err)
+
+		var gotSchemaResp router.GetSchemaResponse
+		err = json.NewDecoder(w.Body).Decode(&gotSchemaResp)
+		assert.NoError(tt, err)
+
+		assert.Equal(tt, createResp.ID, gotSchemaResp.Schema.ID)
+		assert.Equal(tt, createResp.Schema.Schema, gotSchemaResp.Schema.Schema)
+
+		// reset recorder between calls
+		w.Flush()
+
+		// get all schemas - get none
+		req = httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/schemas", nil)
+		err = schemaService.GetSchemas(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+		err = json.NewDecoder(w.Body).Decode(&getSchemasResp)
+		assert.NoError(tt, err)
+		assert.Len(tt, getSchemasResp.Schemas, 1)
+	})
+}
diff --git a/pkg/server/server_test.go b/pkg/server/server_test.go
index 4dcb68b..e42eae7 100644
--- a/pkg/server/server_test.go
+++ b/pkg/server/server_test.go
@@ -3,7 +3,6 @@ package server
 import (
 	"bytes"
 	"context"
-	"fmt"
 	"io"
 	"net/http"
 	"net/http/httptest"
@@ -15,13 +14,11 @@ import (
 
 	"github.com/tbd54566975/ssi-service/pkg/service/dwn"
 
-	credsdk "github.com/TBD54566975/ssi-sdk/credential"
 	manifestsdk "github.com/TBD54566975/ssi-sdk/credential/manifest"
 	"github.com/TBD54566975/ssi-sdk/crypto"
 	"github.com/dimfeld/httptreemux/v5"
 	"github.com/goccy/go-json"
 	"github.com/google/uuid"
-	"github.com/mr-tron/base58"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
@@ -94,1279 +91,6 @@ func TestReadinessAPI(t *testing.T) {
 	assert.Len(t, resp.ServiceStatuses, 0)
 }
 
-func TestDIDAPI(t *testing.T) {
-	t.Run("Test Get DID Methods", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		_, keyStoreService := testKeyStore(tt, bolt)
-		didService := testDIDRouter(tt, bolt, keyStoreService)
-
-		// get DID methods
-		req := httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/dids", nil)
-		w := httptest.NewRecorder()
-
-		err = didService.GetDIDMethods(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-		assert.Equal(tt, http.StatusOK, w.Result().StatusCode)
-
-		var resp router.GetDIDMethodsResponse
-		err = json.NewDecoder(w.Body).Decode(&resp)
-		assert.NoError(tt, err)
-
-		assert.Len(tt, resp.DIDMethods, 1)
-		assert.Equal(tt, resp.DIDMethods[0], did.KeyMethod)
-	})
-
-	t.Run("Test Create DID By Method: Key", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		_, keyStoreService := testKeyStore(tt, bolt)
-		didService := testDIDRouter(tt, bolt, keyStoreService)
-
-		// create DID by method - key - missing body
-		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/dids/key", nil)
-		w := httptest.NewRecorder()
-		params := map[string]string{
-			"method": "key",
-		}
-
-		err = didService.CreateDIDByMethod(newRequestContextWithParams(params), w, req)
-		assert.Error(tt, err)
-		assert.Contains(tt, err.Error(), "invalid create DID request")
-
-		// reset recorder between calls
-		w.Flush()
-
-		// with body, bad key type
-		createDIDRequest := router.CreateDIDByMethodRequest{KeyType: "bad"}
-		requestReader := newRequestValue(tt, createDIDRequest)
-		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/dids/key", requestReader)
-
-		err = didService.CreateDIDByMethod(newRequestContextWithParams(params), w, req)
-		assert.Error(tt, err)
-		assert.Contains(tt, err.Error(), "could not create DID for method<key> with key type: bad")
-
-		// reset recorder between calls
-		w.Flush()
-
-		// with body, good key type
-		createDIDRequest = router.CreateDIDByMethodRequest{KeyType: crypto.Ed25519}
-		requestReader = newRequestValue(tt, createDIDRequest)
-		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/dids/key", requestReader)
-
-		err = didService.CreateDIDByMethod(newRequestContextWithParams(params), w, req)
-		assert.NoError(tt, err)
-
-		var resp router.CreateDIDByMethodResponse
-		err = json.NewDecoder(w.Body).Decode(&resp)
-		assert.NoError(tt, err)
-
-		assert.Contains(tt, resp.DID.ID, did.KeyMethod)
-	})
-
-	t.Run("Test Get DID By Method", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		_, keyStore := testKeyStore(tt, bolt)
-		didService := testDIDRouter(tt, bolt, keyStore)
-
-		// get DID by method
-		req := httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/dids/bad/worse", nil)
-		w := httptest.NewRecorder()
-
-		// bad params
-		badParams := map[string]string{
-			"method": "bad",
-			"id":     "worse",
-		}
-		err = didService.GetDIDByMethod(newRequestContextWithParams(badParams), w, req)
-		assert.Error(tt, err)
-		assert.Contains(tt, err.Error(), "could not get DID for method<bad>")
-
-		// reset recorder between calls
-		w.Flush()
-
-		// good method, bad id
-		badParams1 := map[string]string{
-			"method": "key",
-			"id":     "worse",
-		}
-		err = didService.GetDIDByMethod(newRequestContextWithParams(badParams1), w, req)
-		assert.Error(tt, err)
-		assert.Contains(tt, err.Error(), "could not get DID for method<key> with id: worse")
-
-		// reset recorder between calls
-		w.Flush()
-		// store a DID
-		createDIDRequest := router.CreateDIDByMethodRequest{KeyType: crypto.Ed25519}
-		requestReader := newRequestValue(tt, createDIDRequest)
-		params := map[string]string{"method": "key"}
-		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/dids/key", requestReader)
-
-		err = didService.CreateDIDByMethod(newRequestContextWithParams(params), w, req)
-		assert.NoError(tt, err)
-
-		var createdDID router.CreateDIDByMethodResponse
-		err = json.NewDecoder(w.Body).Decode(&createdDID)
-		assert.NoError(tt, err)
-
-		// reset recorder between calls
-		w.Flush()
-
-		// get it back
-		createdID := createdDID.DID.ID
-		getDIDPath := fmt.Sprintf("https://ssi-service.com/v1/dids/key/%s", createdID)
-		req = httptest.NewRequest(http.MethodGet, getDIDPath, nil)
-
-		// good params
-		goodParams := map[string]string{
-			"method": "key",
-			"id":     createdID,
-		}
-		err = didService.GetDIDByMethod(newRequestContextWithParams(goodParams), w, req)
-		assert.NoError(tt, err)
-
-		var resp router.GetDIDByMethodResponse
-		err = json.NewDecoder(w.Body).Decode(&resp)
-		assert.NoError(tt, err)
-		assert.Equal(tt, createdID, resp.DID.ID)
-	})
-
-	t.Run("Test Get DIDs By Method", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		_, keyStore := testKeyStore(tt, bolt)
-		didService := testDIDRouter(tt, bolt, keyStore)
-
-		// get DIDs by method
-		req := httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/dids/bad", nil)
-		w := httptest.NewRecorder()
-
-		// bad params
-		badParams := map[string]string{
-			"method": "bad",
-		}
-		err = didService.GetDIDsByMethod(newRequestContextWithParams(badParams), w, req)
-		assert.Error(tt, err)
-		assert.Contains(tt, err.Error(), "could not get DIDs for method: bad")
-
-		// good method
-		goodParams := map[string]string{
-			"method": "key",
-		}
-		err = didService.GetDIDsByMethod(newRequestContextWithParams(goodParams), w, req)
-		assert.NoError(tt, err)
-		var gotDIDs router.GetDIDByMethodResponse
-		err = json.NewDecoder(w.Body).Decode(&gotDIDs)
-		assert.NoError(tt, err)
-		assert.Empty(tt, gotDIDs)
-
-		// reset recorder between calls
-		w.Flush()
-
-		// store two DIDs
-		createDIDRequest := router.CreateDIDByMethodRequest{KeyType: crypto.Ed25519}
-		requestReader := newRequestValue(tt, createDIDRequest)
-		params := map[string]string{"method": "key"}
-		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/dids/key", requestReader)
-
-		err = didService.CreateDIDByMethod(newRequestContextWithParams(params), w, req)
-		assert.NoError(tt, err)
-
-		var createdDID router.CreateDIDByMethodResponse
-		err = json.NewDecoder(w.Body).Decode(&createdDID)
-		assert.NoError(tt, err)
-
-		// reset recorder between calls
-		w.Flush()
-
-		requestReader = newRequestValue(tt, createDIDRequest)
-		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/dids/key", requestReader)
-
-		err = didService.CreateDIDByMethod(newRequestContextWithParams(params), w, req)
-		assert.NoError(tt, err)
-		
-		var createdDID2 router.CreateDIDByMethodResponse
-		err = json.NewDecoder(w.Body).Decode(&createdDID2)
-		assert.NoError(tt, err)
-
-		// reset recorder between calls
-		w.Flush()
-
-		// get all dids for method
-
-		req = httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/dids/key", requestReader)
-		err = didService.GetDIDsByMethod(newRequestContextWithParams(params), w, req)
-		assert.NoError(tt, err)
-
-		var gotDIDsResponse router.GetDIDsByMethodResponse
-		err = json.NewDecoder(w.Body).Decode(&gotDIDsResponse)
-		assert.NoError(tt, err)
-
-		knownDIDs := map[string]bool{createdDID.DID.ID: true, createdDID2.DID.ID: true}
-		for _, did := range gotDIDsResponse.DIDs {
-			if _, ok := knownDIDs[did.ID]; !ok {
-				tt.Error("got unknown DID")
-			} else {
-				delete(knownDIDs, did.ID)
-			}
-		}
-		assert.Len(tt, knownDIDs, 0)
-	})
-}
-
-func TestSchemaAPI(t *testing.T) {
-	t.Run("Test Create Schema", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		keyStoreService := testKeyStoreService(tt, bolt)
-		schemaService := testSchemaRouter(tt, bolt, keyStoreService)
-
-		simpleSchema := map[string]interface{}{
-			"type": "object",
-			"properties": map[string]interface{}{
-				"foo": map[string]interface{}{
-					"type": "string",
-				},
-			},
-			"required":             []interface{}{"foo"},
-			"additionalProperties": false,
-		}
-		badSchemaRequest := router.CreateSchemaRequest{Schema: simpleSchema}
-		schemaRequestValue := newRequestValue(tt, badSchemaRequest)
-		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/schemas", schemaRequestValue)
-		w := httptest.NewRecorder()
-
-		err = schemaService.CreateSchema(newRequestContext(), w, req)
-		assert.Error(tt, err)
-		assert.Contains(tt, err.Error(), "invalid create schema request")
-
-		// reset the http recorder
-		w.Flush()
-
-		schemaRequest := router.CreateSchemaRequest{Author: "did:test", Name: "test schema", Schema: simpleSchema}
-		schemaRequestValue = newRequestValue(tt, schemaRequest)
-		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/schemas", schemaRequestValue)
-		err = schemaService.CreateSchema(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-
-		var resp router.CreateSchemaResponse
-		err = json.NewDecoder(w.Body).Decode(&resp)
-		assert.NoError(tt, err)
-
-		assert.NotEmpty(tt, resp.ID)
-		assert.EqualValues(tt, schemaRequest.Schema, resp.Schema.Schema)
-	})
-
-	t.Run("Test Get Schemas", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		keyStoreService := testKeyStoreService(tt, bolt)
-		schemaService := testSchemaRouter(tt, bolt, keyStoreService)
-
-		// get schema that doesn't exist
-		w := httptest.NewRecorder()
-		req := httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/schemas/bad", nil)
-		err = schemaService.GetSchema(newRequestContext(), w, req)
-		assert.Error(tt, err)
-		assert.Contains(tt, err.Error(), "cannot get schema without ID parameter")
-
-		// reset recorder between calls
-		w.Flush()
-
-		// get schema with invalid id
-		req = httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/schemas/bad", nil)
-		err = schemaService.GetSchema(newRequestContextWithParams(map[string]string{"id": "bad"}), w, req)
-		assert.Error(tt, err)
-		assert.Contains(tt, err.Error(), "could not get schema with id: bad")
-
-		// reset recorder between calls
-		w.Flush()
-
-		// get all schemas - get none
-		req = httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/schemas", nil)
-		err = schemaService.GetSchemas(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-		var getSchemasResp router.GetSchemasResponse
-		err = json.NewDecoder(w.Body).Decode(&getSchemasResp)
-		assert.NoError(tt, err)
-		assert.Len(tt, getSchemasResp.Schemas, 0)
-
-		// reset recorder between calls
-		w.Flush()
-
-		// create a schema
-		simpleSchema := map[string]interface{}{
-			"type": "object",
-			"properties": map[string]interface{}{
-				"foo": map[string]interface{}{
-					"type": "string",
-				},
-			},
-			"required":             []interface{}{"foo"},
-			"additionalProperties": false,
-		}
-		schemaRequest := router.CreateSchemaRequest{Author: "did:test", Name: "test schema", Schema: simpleSchema}
-		schemaRequestValue := newRequestValue(tt, schemaRequest)
-		createReq := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/schemas", schemaRequestValue)
-
-		err = schemaService.CreateSchema(newRequestContext(), w, createReq)
-		assert.NoError(tt, err)
-
-		var createResp router.CreateSchemaResponse
-		err = json.NewDecoder(w.Body).Decode(&createResp)
-		assert.NoError(tt, err)
-
-		assert.NotEmpty(tt, createResp.ID)
-		assert.EqualValues(tt, schemaRequest.Schema, createResp.Schema.Schema)
-
-		// reset recorder between calls
-		w.Flush()
-
-		// get it back
-		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/schemas/%s", createResp.ID), nil)
-		err = schemaService.GetSchema(newRequestContextWithParams(map[string]string{"id": createResp.ID}), w, req)
-		assert.NoError(tt, err)
-
-		var gotSchemaResp router.GetSchemaResponse
-		err = json.NewDecoder(w.Body).Decode(&gotSchemaResp)
-		assert.NoError(tt, err)
-
-		assert.Equal(tt, createResp.ID, gotSchemaResp.Schema.ID)
-		assert.Equal(tt, createResp.Schema.Schema, gotSchemaResp.Schema.Schema)
-
-		// reset recorder between calls
-		w.Flush()
-
-		// get all schemas - get none
-		req = httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/schemas", nil)
-		err = schemaService.GetSchemas(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-		err = json.NewDecoder(w.Body).Decode(&getSchemasResp)
-		assert.NoError(tt, err)
-		assert.Len(tt, getSchemasResp.Schemas, 1)
-	})
-}
-
-func TestCredentialAPI(t *testing.T) {
-	t.Run("Test Create Credential", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		keyStoreService := testKeyStoreService(tt, bolt)
-		credService := testCredentialRouter(tt, bolt, keyStoreService)
-
-		// missing required field: data
-		badCredRequest := router.CreateCredentialRequest{
-			Issuer:  "did:abc:123",
-			Subject: "did:abc:456",
-			Expiry:  time.Now().Add(24 * time.Hour).Format(time.RFC3339),
-		}
-		badRequestValue := newRequestValue(tt, badCredRequest)
-		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/credentials", badRequestValue)
-		w := httptest.NewRecorder()
-
-		err = credService.CreateCredential(newRequestContext(), w, req)
-		assert.Error(tt, err)
-		assert.Contains(tt, err.Error(), "invalid create credential request")
-
-		// reset the http recorder
-		w.Flush()
-
-		// good request
-		createCredRequest := router.CreateCredentialRequest{
-			Issuer:  "did:abc:123",
-			Subject: "did:abc:456",
-			Data: map[string]interface{}{
-				"firstName": "Jack",
-				"lastName":  "Dorsey",
-			},
-			Expiry: time.Now().Add(24 * time.Hour).Format(time.RFC3339),
-		}
-		requestValue := newRequestValue(tt, createCredRequest)
-		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/credentials", requestValue)
-		err = credService.CreateCredential(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-
-		var resp router.CreateCredentialResponse
-		err = json.NewDecoder(w.Body).Decode(&resp)
-		assert.NoError(tt, err)
-
-		assert.NotEmpty(tt, resp.Credential)
-		assert.Equal(tt, resp.Credential.Issuer, "did:abc:123")
-	})
-
-	t.Run("Test Get Credential By ID", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		keyStoreService := testKeyStoreService(tt, bolt)
-		credService := testCredentialRouter(tt, bolt, keyStoreService)
-
-		w := httptest.NewRecorder()
-
-		// get a cred that doesn't exit
-		req := httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/credentials/bad", nil)
-		err = credService.GetCredential(newRequestContext(), w, req)
-		assert.Error(tt, err)
-		assert.Contains(tt, err.Error(), "cannot get credential without ID parameter")
-
-		// reset recorder between calls
-		w.Flush()
-
-		// get a cred with an invalid id parameter
-		req = httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/credentials/bad", nil)
-		err = credService.GetCredential(newRequestContextWithParams(map[string]string{"id": "bad"}), w, req)
-		assert.Error(tt, err)
-		assert.Contains(tt, err.Error(), "could not get credential with id: bad")
-
-		// reset recorder between calls
-		w.Flush()
-
-		createCredRequest := router.CreateCredentialRequest{
-			Issuer:  "did:abc:123",
-			Subject: "did:abc:456",
-			Data: map[string]interface{}{
-				"firstName": "Jack",
-				"lastName":  "Dorsey",
-			},
-			Expiry: time.Now().Add(24 * time.Hour).Format(time.RFC3339),
-		}
-		requestValue := newRequestValue(tt, createCredRequest)
-		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/credentials", requestValue)
-		err = credService.CreateCredential(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-
-		var resp router.CreateCredentialResponse
-		err = json.NewDecoder(w.Body).Decode(&resp)
-		assert.NoError(tt, err)
-
-		// get credential by id
-		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/credentials/%s", resp.Credential.ID), nil)
-		err = credService.GetCredential(newRequestContextWithParams(map[string]string{"id": resp.Credential.ID}), w, req)
-		assert.NoError(tt, err)
-
-		var getCredResp router.GetCredentialResponse
-		err = json.NewDecoder(w.Body).Decode(&getCredResp)
-		assert.NoError(tt, err)
-		assert.NotEmpty(tt, getCredResp)
-		assert.Equal(tt, resp.Credential.ID, getCredResp.ID)
-	})
-
-	t.Run("Test Get Credential By Schema", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		keyStoreService := testKeyStoreService(tt, bolt)
-		credService := testCredentialRouter(tt, bolt, keyStoreService)
-
-		w := httptest.NewRecorder()
-
-		schemaID := "https://test-schema.com/name"
-		createCredRequest := router.CreateCredentialRequest{
-			Issuer:  "did:abc:123",
-			Subject: "did:abc:456",
-			Schema:  schemaID,
-			Data: map[string]interface{}{
-				"firstName": "Jack",
-				"lastName":  "Dorsey",
-			},
-			Expiry: time.Now().Add(24 * time.Hour).Format(time.RFC3339),
-		}
-		requestValue := newRequestValue(tt, createCredRequest)
-		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/credentials", requestValue)
-		err = credService.CreateCredential(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-
-		var resp router.CreateCredentialResponse
-		err = json.NewDecoder(w.Body).Decode(&resp)
-		assert.NoError(tt, err)
-
-		w.Flush()
-
-		// get credential by schema
-		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/credential?schema=%s", schemaID), nil)
-		err = credService.GetCredentials(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-
-		var getCredsResp router.GetCredentialsResponse
-		err = json.NewDecoder(w.Body).Decode(&getCredsResp)
-		assert.NoError(tt, err)
-		assert.NotEmpty(tt, getCredsResp)
-		assert.Len(tt, getCredsResp.Credentials, 1)
-		assert.Equal(tt, resp.Credential.ID, getCredsResp.Credentials[0].ID)
-		assert.Equal(tt, resp.Credential.CredentialSchema.ID, getCredsResp.Credentials[0].CredentialSchema.ID)
-	})
-
-	t.Run("Test Get Credential By Issuer", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		keyStoreService := testKeyStoreService(tt, bolt)
-		credService := testCredentialRouter(tt, bolt, keyStoreService)
-
-		w := httptest.NewRecorder()
-
-		issuerID := "did:abc:123"
-		createCredRequest := router.CreateCredentialRequest{
-			Issuer:  issuerID,
-			Subject: "did:abc:456",
-			Data: map[string]interface{}{
-				"firstName": "Jack",
-				"lastName":  "Dorsey",
-			},
-			Expiry: time.Now().Add(24 * time.Hour).Format(time.RFC3339),
-		}
-		requestValue := newRequestValue(tt, createCredRequest)
-		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/credentials", requestValue)
-		err = credService.CreateCredential(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-
-		var resp router.CreateCredentialResponse
-		err = json.NewDecoder(w.Body).Decode(&resp)
-		assert.NoError(tt, err)
-
-		w.Flush()
-
-		// get credential by issuer id
-		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/credential?issuer=%s", issuerID), nil)
-		err = credService.GetCredentials(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-
-		var getCredsResp router.GetCredentialsResponse
-		err = json.NewDecoder(w.Body).Decode(&getCredsResp)
-		assert.NoError(tt, err)
-		assert.NotEmpty(tt, getCredsResp)
-		assert.Len(tt, getCredsResp.Credentials, 1)
-		assert.Equal(tt, resp.Credential.ID, getCredsResp.Credentials[0].ID)
-		assert.Equal(tt, resp.Credential.Issuer, getCredsResp.Credentials[0].Issuer)
-	})
-
-	t.Run("Test Get Credential By Subject", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		keyStoreService := testKeyStoreService(tt, bolt)
-		credService := testCredentialRouter(tt, bolt, keyStoreService)
-
-		w := httptest.NewRecorder()
-
-		subjectID := "did:abc:456"
-		createCredRequest := router.CreateCredentialRequest{
-			Issuer:  "did:abc:123",
-			Subject: subjectID,
-			Data: map[string]interface{}{
-				"firstName": "Jack",
-				"lastName":  "Dorsey",
-			},
-			Expiry: time.Now().Add(24 * time.Hour).Format(time.RFC3339),
-		}
-		requestValue := newRequestValue(tt, createCredRequest)
-		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/credentials", requestValue)
-		err = credService.CreateCredential(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-
-		var resp router.CreateCredentialResponse
-		err = json.NewDecoder(w.Body).Decode(&resp)
-		assert.NoError(tt, err)
-
-		// get credential by subject id
-		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/credential?subject=%s", subjectID), nil)
-		err = credService.GetCredentials(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-
-		var getCredsResp router.GetCredentialsResponse
-		err = json.NewDecoder(w.Body).Decode(&getCredsResp)
-		assert.NoError(tt, err)
-		assert.NotEmpty(tt, getCredsResp)
-		assert.Len(tt, getCredsResp.Credentials, 1)
-		assert.Equal(tt, resp.Credential.ID, getCredsResp.Credentials[0].ID)
-		assert.Equal(tt, resp.Credential.CredentialSubject[credsdk.VerifiableCredentialIDProperty], getCredsResp.Credentials[0].CredentialSubject[credsdk.VerifiableCredentialIDProperty])
-	})
-
-	t.Run("Test Delete Credential", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		keyStoreService := testKeyStoreService(tt, bolt)
-		credService := testCredentialRouter(tt, bolt, keyStoreService)
-
-		createCredRequest := router.CreateCredentialRequest{
-			Issuer:  "did:abc:123",
-			Subject: "did:abc:456",
-			Data: map[string]interface{}{
-				"firstName": "Jack",
-				"lastName":  "Dorsey",
-			},
-			Expiry: time.Now().Add(24 * time.Hour).Format(time.RFC3339),
-		}
-		requestValue := newRequestValue(tt, createCredRequest)
-		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/credentials", requestValue)
-		w := httptest.NewRecorder()
-		err = credService.CreateCredential(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-
-		var resp router.CreateCredentialResponse
-		err = json.NewDecoder(w.Body).Decode(&resp)
-		assert.NoError(tt, err)
-
-		w.Flush()
-
-		// get credential by id
-		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/credentials/%s", resp.Credential.ID), nil)
-		err = credService.GetCredential(newRequestContextWithParams(map[string]string{"id": resp.Credential.ID}), w, req)
-		assert.NoError(tt, err)
-
-		var getCredResp router.GetCredentialResponse
-		err = json.NewDecoder(w.Body).Decode(&getCredResp)
-		assert.NoError(tt, err)
-		assert.NotEmpty(tt, getCredResp)
-		assert.Equal(tt, resp.Credential.ID, getCredResp.ID)
-
-		w.Flush()
-
-		// delete it
-		req = httptest.NewRequest(http.MethodDelete, fmt.Sprintf("https://ssi-service.com/v1/credentials/%s", resp.Credential.ID), nil)
-		err = credService.DeleteCredential(newRequestContextWithParams(map[string]string{"id": resp.Credential.ID}), w, req)
-		assert.NoError(tt, err)
-
-		w.Flush()
-
-		// get it back
-		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/credentials/%s", resp.Credential.ID), nil)
-		err = credService.GetCredential(newRequestContextWithParams(map[string]string{"id": resp.Credential.ID}), w, req)
-		assert.Error(tt, err)
-		assert.Contains(tt, err.Error(), fmt.Sprintf("could not get credential with id: %s", resp.Credential.ID))
-	})
-}
-
-func TestManifestAPI(t *testing.T) {
-	t.Run("Test Create Manifest", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		keyStoreService := testKeyStoreService(tt, bolt)
-		credentialService := testCredentialService(tt, bolt, keyStoreService)
-		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
-
-		// missing required field: Manifest
-		badManifestRequest := router.CreateManifestRequest{}
-
-		badRequestValue := newRequestValue(tt, badManifestRequest)
-		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests", badRequestValue)
-		w := httptest.NewRecorder()
-
-		err = manifestRouter.CreateManifest(newRequestContext(), w, req)
-		assert.Error(tt, err)
-		assert.Contains(tt, err.Error(), "invalid create manifest request")
-
-		// reset the http recorder
-		w.Flush()
-
-		// good request
-		createManifestRequest := getValidManifestRequest()
-
-		requestValue := newRequestValue(tt, createManifestRequest)
-		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests", requestValue)
-		err = manifestRouter.CreateManifest(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-
-		var resp router.CreateManifestResponse
-		err = json.NewDecoder(w.Body).Decode(&resp)
-		assert.NoError(tt, err)
-
-		assert.NotEmpty(tt, resp.Manifest)
-		assert.Equal(tt, resp.Manifest.Issuer.ID, "did:abc:123")
-	})
-
-	t.Run("Test Get Manifest By ID", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		keyStoreService := testKeyStoreService(tt, bolt)
-		credentialService := testCredentialService(tt, bolt, keyStoreService)
-		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
-
-		w := httptest.NewRecorder()
-
-		// get a manifest that doesn't exit
-		req := httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/manifests/bad", nil)
-		err = manifestRouter.GetManifest(newRequestContext(), w, req)
-		assert.Error(tt, err)
-		assert.Contains(tt, err.Error(), "cannot get manifest without ID parameter")
-
-		// reset recorder between calls
-		w.Flush()
-
-		// get a manifest with an invalid id parameter
-		req = httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/manifests/bad", nil)
-		err = manifestRouter.GetManifest(newRequestContextWithParams(map[string]string{"id": "bad"}), w, req)
-		assert.Error(tt, err)
-		assert.Contains(tt, err.Error(), "could not get manifest with id: bad")
-
-		// reset recorder between calls
-		w.Flush()
-
-		// good request
-		createManifestRequest := getValidManifestRequest()
-
-		requestValue := newRequestValue(tt, createManifestRequest)
-		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests", requestValue)
-		err = manifestRouter.CreateManifest(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-
-		var resp router.CreateManifestResponse
-		err = json.NewDecoder(w.Body).Decode(&resp)
-		assert.NoError(tt, err)
-
-		// get manifest by id
-		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/manifests/%s", resp.Manifest.ID), nil)
-		err = manifestRouter.GetManifest(newRequestContextWithParams(map[string]string{"id": resp.Manifest.ID}), w, req)
-		assert.NoError(tt, err)
-
-		var getManifestResp router.GetManifestResponse
-		err = json.NewDecoder(w.Body).Decode(&getManifestResp)
-		assert.NoError(tt, err)
-		assert.NotEmpty(tt, getManifestResp)
-		assert.Equal(tt, resp.Manifest.ID, getManifestResp.ID)
-	})
-
-	t.Run("Test Get Manifests", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		keyStoreService := testKeyStoreService(tt, bolt)
-		credentialService := testCredentialService(tt, bolt, keyStoreService)
-		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
-
-		w := httptest.NewRecorder()
-
-		// good request
-		createManifestRequest := getValidManifestRequest()
-
-		requestValue := newRequestValue(tt, createManifestRequest)
-		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests", requestValue)
-		err = manifestRouter.CreateManifest(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-
-		var resp router.CreateManifestResponse
-		err = json.NewDecoder(w.Body).Decode(&resp)
-		assert.NoError(tt, err)
-
-		// get manifest by id
-		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/manifests"), nil)
-		err = manifestRouter.GetManifests(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-
-		var getManifestsResp router.GetManifestsResponse
-		err = json.NewDecoder(w.Body).Decode(&getManifestsResp)
-		assert.NoError(tt, err)
-		assert.NotEmpty(tt, getManifestsResp)
-		assert.Len(tt, getManifestsResp.Manifests, 1)
-		assert.Equal(tt, resp.Manifest.ID, getManifestsResp.Manifests[0].ID)
-	})
-
-	t.Run("Test Delete Manifest", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		keyStoreService := testKeyStoreService(tt, bolt)
-		credentialService := testCredentialService(tt, bolt, keyStoreService)
-		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
-
-		// good request
-		createManifestRequest := getValidManifestRequest()
-
-		requestValue := newRequestValue(tt, createManifestRequest)
-		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests", requestValue)
-		w := httptest.NewRecorder()
-		err = manifestRouter.CreateManifest(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-
-		var resp router.CreateManifestResponse
-		err = json.NewDecoder(w.Body).Decode(&resp)
-		assert.NoError(tt, err)
-
-		w.Flush()
-
-		// get credential by id
-		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/manifests/%s", resp.Manifest.ID), nil)
-		err = manifestRouter.GetManifest(newRequestContextWithParams(map[string]string{"id": resp.Manifest.ID}), w, req)
-		assert.NoError(tt, err)
-
-		var getManifestResp router.GetCredentialResponse
-		err = json.NewDecoder(w.Body).Decode(&getManifestResp)
-		assert.NoError(tt, err)
-		assert.NotEmpty(tt, getManifestResp)
-		assert.Equal(tt, resp.Manifest.ID, getManifestResp.ID)
-
-		w.Flush()
-
-		// delete it
-		req = httptest.NewRequest(http.MethodDelete, fmt.Sprintf("https://ssi-service.com/v1/manifests/%s", resp.Manifest.ID), nil)
-		err = manifestRouter.DeleteManifest(newRequestContextWithParams(map[string]string{"id": resp.Manifest.ID}), w, req)
-		assert.NoError(tt, err)
-
-		w.Flush()
-
-		// get it back
-		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/manifests/%s", resp.Manifest.ID), nil)
-		err = manifestRouter.GetManifest(newRequestContextWithParams(map[string]string{"id": resp.Manifest.ID}), w, req)
-		assert.Error(tt, err)
-		assert.Contains(tt, err.Error(), fmt.Sprintf("could not get manifest with id: %s", resp.Manifest.ID))
-	})
-
-	t.Run("Test Submit Application", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		keyStoreService := testKeyStoreService(tt, bolt)
-		credentialService := testCredentialService(tt, bolt, keyStoreService)
-		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
-
-		// missing required field: Application
-		badManifestRequest := router.SubmitApplicationRequest{
-			RequesterDID: "id123",
-		}
-
-		badRequestValue := newRequestValue(tt, badManifestRequest)
-		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests/applications", badRequestValue)
-		w := httptest.NewRecorder()
-
-		err = manifestRouter.SubmitApplication(newRequestContext(), w, req)
-		assert.Error(tt, err)
-		assert.Contains(tt, err.Error(), "invalid submit application request")
-
-		// reset the http recorder
-		w.Flush()
-
-		// good request
-		createManifestRequest := getValidManifestRequest()
-
-		requestValue := newRequestValue(tt, createManifestRequest)
-		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests", requestValue)
-		err = manifestRouter.CreateManifest(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-
-		var resp router.CreateManifestResponse
-		err = json.NewDecoder(w.Body).Decode(&resp)
-		assert.NoError(tt, err)
-
-		assert.NotEmpty(tt, resp.Manifest)
-		assert.Equal(tt, resp.Manifest.Issuer.ID, "did:abc:123")
-
-		// good application request
-		createApplicationRequest := getValidApplicationRequest(resp.Manifest.ID, resp.Manifest.PresentationDefinition.InputDescriptors[0].ID)
-
-		applicationRequestValue := newRequestValue(tt, createApplicationRequest)
-		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests/applications", applicationRequestValue)
-		err = manifestRouter.SubmitApplication(newRequestContext(), w, req)
-
-		var appResp router.SubmitApplicationResponse
-		err = json.NewDecoder(w.Body).Decode(&appResp)
-		assert.NoError(tt, err)
-
-		assert.NotEmpty(tt, appResp.Response)
-		assert.Equal(tt, appResp.Response.ManifestID, resp.Manifest.ID)
-
-	})
-
-	t.Run("Test Get Application By ID and Get Applications", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		keyStoreService := testKeyStoreService(tt, bolt)
-		credentialService := testCredentialService(tt, bolt, keyStoreService)
-		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
-		w := httptest.NewRecorder()
-
-		// get a application that doesn't exit
-		req := httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/manifests/applications/bad", nil)
-		err = manifestRouter.GetApplication(newRequestContext(), w, req)
-		assert.Error(tt, err)
-		assert.Contains(tt, err.Error(), "cannot get application without ID parameter")
-
-		// reset recorder between calls
-		w.Flush()
-
-		// good manifest request
-		createManifestRequest := getValidManifestRequest()
-
-		requestValue := newRequestValue(tt, createManifestRequest)
-		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests", requestValue)
-		err = manifestRouter.CreateManifest(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-
-		var resp router.CreateManifestResponse
-		err = json.NewDecoder(w.Body).Decode(&resp)
-		assert.NoError(tt, err)
-
-		// good application request
-		createApplicationRequest := getValidApplicationRequest(resp.Manifest.ID, resp.Manifest.PresentationDefinition.InputDescriptors[0].ID)
-
-		applicationRequestValue := newRequestValue(tt, createApplicationRequest)
-		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests/applications", applicationRequestValue)
-		err = manifestRouter.SubmitApplication(newRequestContext(), w, req)
-
-		var appResp router.SubmitApplicationResponse
-		err = json.NewDecoder(w.Body).Decode(&appResp)
-		assert.NoError(tt, err)
-
-		// get response by id
-		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/manifests/responses/%s", appResp.Response.ID), nil)
-		err = manifestRouter.GetResponse(newRequestContextWithParams(map[string]string{"id": appResp.Response.ID}), w, req)
-		assert.NoError(tt, err)
-
-		var getResponseResponse router.GetResponseResponse
-		err = json.NewDecoder(w.Body).Decode(&getResponseResponse)
-		assert.NoError(tt, err)
-		assert.NotEmpty(tt, getResponseResponse)
-		assert.Equal(tt, appResp.Response.ID, getResponseResponse.Response.ID)
-
-		// get all responses
-		req = httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/manifests/responses", nil)
-		err = manifestRouter.GetResponses(newRequestContext(), w, req)
-
-		var getResponsesResp router.GetResponsesResponse
-		err = json.NewDecoder(w.Body).Decode(&getResponsesResp)
-		assert.NoError(tt, err)
-		assert.NotEmpty(tt, getResponsesResp)
-
-		assert.Len(tt, getResponsesResp.Responses, 1)
-
-		// get all applications
-		req = httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/manifests/applications", applicationRequestValue)
-		err = manifestRouter.GetApplications(newRequestContext(), w, req)
-
-		var getApplicationsResp router.GetApplicationsResponse
-		err = json.NewDecoder(w.Body).Decode(&getApplicationsResp)
-		assert.NoError(tt, err)
-		assert.NotEmpty(tt, getApplicationsResp)
-
-		assert.Len(tt, getApplicationsResp.Applications, 1)
-
-		// get application by id
-		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/manifests/applications/%s", getApplicationsResp.Applications[0].ID), nil)
-		err = manifestRouter.GetApplication(newRequestContextWithParams(map[string]string{"id": getApplicationsResp.Applications[0].ID}), w, req)
-		assert.NoError(tt, err)
-
-		var getApplicationResponse router.GetApplicationResponse
-		err = json.NewDecoder(w.Body).Decode(&getApplicationResponse)
-		assert.NoError(tt, err)
-		assert.NotEmpty(tt, getApplicationResponse)
-		assert.Equal(tt, getApplicationsResp.Applications[0].ID, getApplicationResponse.ID)
-	})
-
-	t.Run("Test Delete Application", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		keyStoreService := testKeyStoreService(tt, bolt)
-		credentialService := testCredentialService(tt, bolt, keyStoreService)
-		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
-
-		// good manifest request
-		createManifestRequest := getValidManifestRequest()
-
-		requestValue := newRequestValue(tt, createManifestRequest)
-		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests", requestValue)
-		w := httptest.NewRecorder()
-		err = manifestRouter.CreateManifest(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-
-		var resp router.CreateManifestResponse
-		err = json.NewDecoder(w.Body).Decode(&resp)
-		assert.NoError(tt, err)
-
-		// good application request
-		createApplicationRequest := getValidApplicationRequest(resp.Manifest.ID, resp.Manifest.PresentationDefinition.InputDescriptors[0].ID)
-
-		applicationRequestValue := newRequestValue(tt, createApplicationRequest)
-		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests/applications", applicationRequestValue)
-		err = manifestRouter.SubmitApplication(newRequestContext(), w, req)
-
-		var appResp router.SubmitApplicationResponse
-		err = json.NewDecoder(w.Body).Decode(&appResp)
-		assert.NoError(tt, err)
-
-		// get all applications
-		req = httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/manifests/applications", applicationRequestValue)
-		err = manifestRouter.GetApplications(newRequestContext(), w, req)
-
-		var getApplicationsResp router.GetApplicationsResponse
-		err = json.NewDecoder(w.Body).Decode(&getApplicationsResp)
-		assert.NoError(tt, err)
-		assert.NotEmpty(tt, getApplicationsResp)
-
-		assert.Len(tt, getApplicationsResp.Applications, 1)
-
-		// get the application
-		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/manifests/applications/%s", getApplicationsResp.Applications[0].ID), nil)
-		err = manifestRouter.GetApplication(newRequestContextWithParams(map[string]string{"id": getApplicationsResp.Applications[0].ID}), w, req)
-		assert.NoError(tt, err)
-
-		var getApplicationResp router.GetApplicationResponse
-		err = json.NewDecoder(w.Body).Decode(&getApplicationResp)
-		assert.NoError(tt, err)
-		assert.NotEmpty(tt, getApplicationResp)
-		assert.Equal(tt, resp.Manifest.ID, getApplicationResp.Application.ManifestID)
-
-		// delete the application
-		req = httptest.NewRequest(http.MethodDelete, fmt.Sprintf("https://ssi-service.com/v1/manifests/applications/%s", getApplicationResp.Application.ID), nil)
-		err = manifestRouter.DeleteApplication(newRequestContextWithParams(map[string]string{"id": getApplicationResp.Application.ID}), w, req)
-		assert.NoError(tt, err)
-
-		w.Flush()
-
-		// get it back
-		req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/manifests/applications/%s", appResp.Response.ID), nil)
-		err = manifestRouter.GetApplication(newRequestContextWithParams(map[string]string{"id": appResp.Response.ID}), w, req)
-		assert.Error(tt, err)
-		assert.Contains(tt, err.Error(), fmt.Sprintf("could not get application with id: %s", appResp.Response.ID))
-	})
-}
-
-func TestDWNAPI(t *testing.T) {
-	t.Run("Test DWN Publish Manifest", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		keyStoreService := testKeyStoreService(tt, bolt)
-		credentialService := testCredentialService(tt, bolt, keyStoreService)
-		manifestRouter, manifestService := testManifest(tt, bolt, keyStoreService, credentialService)
-		dwnService := testDWNRouter(tt, bolt, keyStoreService, manifestService)
-
-		w := httptest.NewRecorder()
-
-		// good request
-		createManifestRequest := getValidManifestRequest()
-
-		requestValue := newRequestValue(tt, createManifestRequest)
-		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests", requestValue)
-		err = manifestRouter.CreateManifest(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-
-		var resp router.CreateManifestResponse
-		err = json.NewDecoder(w.Body).Decode(&resp)
-		assert.NoError(tt, err)
-
-		w = httptest.NewRecorder()
-
-		dwnRequest := router.PublishManifestRequest{ManifestID: "WA-DL-CLASS-A"}
-		dwnRequestValue := newRequestValue(tt, dwnRequest)
-		dwnReq := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/dwn/manifests", dwnRequestValue)
-		err = dwnService.PublishManifest(newRequestContext(), w, dwnReq)
-
-		assert.Error(tt, err)
-		assert.ErrorContains(tt, err, "unsupported protocol scheme")
-	})
-}
-
-func TestKeyStoreAPI(t *testing.T) {
-	t.Run("Test Store Key", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		keyStoreRouter, _ := testKeyStore(tt, bolt)
-		w := httptest.NewRecorder()
-
-		// bad key type
-		badKeyStoreRequest := router.StoreKeyRequest{
-			ID:               "test-kid",
-			Type:             "bad",
-			Controller:       "me",
-			Base58PrivateKey: "bad",
-		}
-		badRequestValue := newRequestValue(tt, badKeyStoreRequest)
-		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/keys", badRequestValue)
-		err = keyStoreRouter.StoreKey(newRequestContext(), w, req)
-		assert.Error(tt, err)
-		assert.Contains(tt, err.Error(), "could not store key: test-kid, unsupported key type: bad")
-
-		// reset the http recorder
-		w.Flush()
-
-		// store a valid key
-		_, privKey, err := crypto.GenerateKeyByKeyType(crypto.Ed25519)
-		assert.NoError(tt, err)
-		assert.NotEmpty(tt, privKey)
-
-		privKeyBytes, err := crypto.PrivKeyToBytes(privKey)
-		assert.NoError(tt, err)
-
-		// good request
-		storeKeyRequest := router.StoreKeyRequest{
-			ID:               "did:test:me#key-1",
-			Type:             crypto.Ed25519,
-			Controller:       "did:test:me",
-			Base58PrivateKey: base58.Encode(privKeyBytes),
-		}
-		requestValue := newRequestValue(tt, storeKeyRequest)
-		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/keys", requestValue)
-		err = keyStoreRouter.StoreKey(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-	})
-
-	t.Run("Test Get Key Details", func(tt *testing.T) {
-		bolt, err := storage.NewBoltDB()
-
-		// remove the db file after the test
-		tt.Cleanup(func() {
-			_ = bolt.Close()
-			_ = os.Remove(storage.DBFile)
-		})
-
-		keyStoreService, _ := testKeyStore(tt, bolt)
-		w := httptest.NewRecorder()
-
-		// store a valid key
-		_, privKey, err := crypto.GenerateKeyByKeyType(crypto.Ed25519)
-		assert.NoError(tt, err)
-		assert.NotEmpty(tt, privKey)
-
-		privKeyBytes, err := crypto.PrivKeyToBytes(privKey)
-		assert.NoError(tt, err)
-
-		// good request
-		keyID := "did:test:me#key-2"
-		controller := "did:test:me"
-		storeKeyRequest := router.StoreKeyRequest{
-			ID:               keyID,
-			Type:             crypto.Ed25519,
-			Controller:       controller,
-			Base58PrivateKey: base58.Encode(privKeyBytes),
-		}
-		requestValue := newRequestValue(tt, storeKeyRequest)
-		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/keys", requestValue)
-		err = keyStoreService.StoreKey(newRequestContext(), w, req)
-		assert.NoError(tt, err)
-
-		// get it back
-		getRecorder := httptest.NewRecorder()
-		getReq := httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://ssi-service.com/v1/keys/%s", keyID), nil)
-		err = keyStoreService.GetKeyDetails(newRequestContextWithParams(map[string]string{"id": keyID}), getRecorder, getReq)
-		assert.NoError(tt, err)
-
-		var resp router.GetKeyDetailsResponse
-		err = json.NewDecoder(getRecorder.Body).Decode(&resp)
-		assert.NoError(tt, err)
-		assert.Equal(tt, keyID, resp.ID)
-		assert.Equal(tt, controller, resp.Controller)
-		assert.Equal(tt, crypto.Ed25519, resp.Type)
-	})
-}
-
 func newRequestValue(t *testing.T, data interface{}) io.Reader {
 	dataBytes, err := json.Marshal(data)
 	require.NoError(t, err)
@@ -1393,13 +117,13 @@ func newRequestContextWithParams(params map[string]string) context.Context {
 	return httptreemux.AddParamsToContext(ctx, params)
 }
 
-func getValidManifestRequest() manifest.CreateManifestRequest {
+func getValidManifestRequest(issuer string) manifest.CreateManifestRequest {
 	createManifestRequest := manifest.CreateManifestRequest{
 		Manifest: manifestsdk.CredentialManifest{
 			ID:          "WA-DL-CLASS-A",
 			SpecVersion: "https://identity.foundation/credential-manifest/spec/v1.0.0/",
 			Issuer: manifestsdk.Issuer{
-				ID: "did:abc:123",
+				ID: issuer,
 			},
 			PresentationDefinition: &exchange.PresentationDefinition{
 				ID: "pres-def-id",
@@ -1436,7 +160,7 @@ func getValidManifestRequest() manifest.CreateManifestRequest {
 	return createManifestRequest
 }
 
-func getValidApplicationRequest(manifestID string, submissionDescriptorId string) manifest.SubmitApplicationRequest {
+func getValidApplicationRequest(applicantDID, manifestID, submissionDescriptorID string) manifest.SubmitApplicationRequest {
 	createApplication := manifestsdk.CredentialApplication{
 		ID:          uuid.New().String(),
 		SpecVersion: "https://identity.foundation/credential-manifest/spec/v1.0.0/",
@@ -1449,7 +173,7 @@ func getValidApplicationRequest(manifestID string, submissionDescriptorId string
 			DefinitionID: "definitionId",
 			DescriptorMap: []exchange.SubmissionDescriptor{
 				{
-					ID:     submissionDescriptorId,
+					ID:     submissionDescriptorID,
 					Format: "jwt",
 					Path:   "path",
 				},
@@ -1459,7 +183,7 @@ func getValidApplicationRequest(manifestID string, submissionDescriptorId string
 
 	createApplicationRequest := manifest.SubmitApplicationRequest{
 		Application:  createApplication,
-		RequesterDID: "did:user:123",
+		ApplicantDID: applicantDID,
 	}
 
 	return createApplicationRequest
diff --git a/pkg/service/keystore/keystore_test.go b/pkg/service/keystore/keystore_test.go
index 1fad6a6..5e7c7cb 100644
--- a/pkg/service/keystore/keystore_test.go
+++ b/pkg/service/keystore/keystore_test.go
@@ -1,11 +1,16 @@
 package keystore
 
 import (
+	"os"
 	"testing"
 
 	"github.com/TBD54566975/ssi-sdk/crypto"
+	"github.com/lestrrat-go/jwx/jwk"
 	"github.com/mr-tron/base58"
 	"github.com/stretchr/testify/assert"
+
+	"github.com/tbd54566975/ssi-service/config"
+	"github.com/tbd54566975/ssi-service/pkg/storage"
 )
 
 func TestGenerateServiceKey(t *testing.T) {
@@ -38,7 +43,7 @@ func TestEncryptDecryptAllKeyTypes(t *testing.T) {
 			kt: crypto.X25519,
 		},
 		{
-			kt: crypto.Secp256k1,
+			kt: crypto.SECP256k1,
 		},
 		{
 			kt: crypto.P224,
@@ -84,3 +89,49 @@ func TestEncryptDecryptAllKeyTypes(t *testing.T) {
 		})
 	}
 }
+
+func TestStoreAndGetKey(t *testing.T) {
+	bolt, err := storage.NewBoltDB()
+	assert.NoError(t, err)
+	assert.NotEmpty(t, bolt)
+
+	// remove the db file after the test
+	t.Cleanup(func() {
+		_ = bolt.Close()
+		_ = os.Remove(storage.DBFile)
+	})
+
+	keyStore, err := NewKeyStoreService(
+		config.KeyStoreServiceConfig{
+			BaseServiceConfig: &config.BaseServiceConfig{
+				Name: "test-keyStore",
+			},
+			ServiceKeyPassword: "test-password",
+		},
+		bolt)
+	assert.NoError(t, err)
+	assert.NotEmpty(t, keyStore)
+
+	// store the key
+	_, privKey, err := crypto.GenerateEd25519Key()
+	err = keyStore.StoreKey(StoreKeyRequest{
+		ID:         "test-id",
+		Type:       crypto.Ed25519,
+		Controller: "test-controller",
+		Key:        privKey,
+	})
+	assert.NoError(t, err)
+
+	// get it back
+	keyResponse, err := keyStore.GetKey(GetKeyRequest{ID: "test-id"})
+	assert.NoError(t, err)
+	assert.NotEmpty(t, keyResponse)
+	assert.Equal(t, privKey, keyResponse.Key)
+
+	// make sure can create a signer properly
+	gotJWK, err := jwk.New(keyResponse.Key)
+	assert.NoError(t, err)
+	signer, err := crypto.NewJWTSigner("kid", gotJWK)
+	assert.NoError(t, err)
+	assert.NotEmpty(t, signer)
+}