Repository: TBD54566975/ssi-service
Pull request #129 (2022-10-14 00:00:06)

Support Verifiable Credentials in Cred Manifest Flow
- add vcs to CM flows
- fixed a bug in validating credentials against schemas
- updated tests accordingly

codecov-commenter: # [Codecov](https://codecov.io/gh/TBD54566975/ssi-service/pull/129?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) Report
> Merging [#129](https://codecov.io/gh/TBD54566975/ssi-service/pull/129?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) (44c8645) into [main](https://codecov.io/gh/TBD54566975/ssi-service/commit/7ebcac537cdae1f23ad7ad2e725691041b0ebeef?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) (7ebcac5) will **decrease** coverage by `0.18%`.
> The diff coverage is `0.00%`.

> :exclamation: Current head 44c8645 differs from pull request most recent head 220572d. Consider uploading reports for the commit 220572d to get more accurate results

```diff
@@            Coverage Diff             @@
##             main     #129      +/-   ##
==========================================
- Coverage   28.00%   27.81%   -0.19%     
==========================================
  Files          20       20              
  Lines        1475     1485      +10     
==========================================
  Hits          413      413              
- Misses        995     1005      +10     
  Partials       67       67              
```


| [Impacted Files](https://codecov.io/gh/TBD54566975/ssi-service/pull/129?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/129/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975#diff-cGtnL3NlcnZlci9yb3V0ZXIvY3JlZGVudGlhbC5nbw==) | `5.40% <ø> (ø)` | |
| [pkg/server/router/manifest.go](https://codecov.io/gh/TBD54566975/ssi-service/pull/129/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975#diff-cGtnL3NlcnZlci9yb3V0ZXIvbWFuaWZlc3QuZ28=) | `3.94% <0.00%> (-0.21%)` | :arrow_down: |

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 1fde001..d8ed8e4 100644
--- a/doc/swagger.yaml
+++ b/doc/swagger.yaml
@@ -1,5 +1,5 @@
 definitions:
-  credential.CredentialContainer:
+  credential.Container:
     properties:
       credential:
         $ref: '#/definitions/credential.VerifiableCredential'
@@ -580,7 +580,7 @@ definitions:
     properties:
       credentials:
         items:
-          $ref: '#/definitions/credential.CredentialContainer'
+          $ref: '#/definitions/credential.Container'
         type: array
     type: object
   github.com_tbd54566975_ssi-service_pkg_server_router.GetDIDByMethodResponse:
@@ -708,11 +708,15 @@ definitions:
         description: Once we have JWT signed wrapper that can get the did this can
           be removed
         type: string
-      application:
+      credential_application:
         $ref: '#/definitions/manifest.CredentialApplication'
+      verifiableCredentials:
+        items: {}
+        type: array
     required:
     - applicantDid
-    - application
+    - credential_application
+    - verifiableCredentials
     type: object
   github.com_tbd54566975_ssi-service_pkg_server_router.SubmitApplicationResponse:
     properties:
@@ -971,7 +975,7 @@ definitions:
     properties:
       credentials:
         items:
-          $ref: '#/definitions/credential.CredentialContainer'
+          $ref: '#/definitions/credential.Container'
         type: array
     type: object
   pkg_server_router.GetDIDByMethodResponse:
@@ -1099,11 +1103,15 @@ definitions:
         description: Once we have JWT signed wrapper that can get the did this can
           be removed
         type: string
-      application:
+      credential_application:
         $ref: '#/definitions/manifest.CredentialApplication'
+      verifiableCredentials:
+        items: {}
+        type: array
     required:
     - applicantDid
-    - application
+    - credential_application
+    - verifiableCredentials
     type: object
   pkg_server_router.SubmitApplicationResponse:
     properties:
@@ -1252,7 +1260,7 @@ definitions:
       version:
         type: string
     type: object
-host: localhost:3000
+host: localhost:8080
 info:
   contact:
     email: tbd-developer@squareup.com
@@ -1276,7 +1284,7 @@ paths:
         "200":
           description: OK
           schema:
-            $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetHealthCheckResponse'
+            $ref: '#/definitions/pkg_server_router.GetHealthCheckResponse'
       summary: Health Check
       tags:
       - HealthCheck
@@ -1322,7 +1330,7 @@ paths:
         "200":
           description: OK
           schema:
-            $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetCredentialsResponse'
+            $ref: '#/definitions/pkg_server_router.GetCredentialsResponse'
         "400":
           description: Bad request
           schema:
@@ -1344,14 +1352,14 @@ paths:
         name: request
         required: true
         schema:
-          $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateCredentialRequest'
+          $ref: '#/definitions/pkg_server_router.CreateCredentialRequest'
       produces:
       - application/json
       responses:
         "201":
           description: Created
           schema:
-            $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateCredentialResponse'
+            $ref: '#/definitions/pkg_server_router.CreateCredentialResponse'
         "400":
           description: Bad request
           schema:
@@ -1408,7 +1416,7 @@ paths:
         "200":
           description: OK
           schema:
-            $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetCredentialResponse'
+            $ref: '#/definitions/pkg_server_router.GetCredentialResponse'
         "400":
           description: Bad request
           schema:
@@ -1427,14 +1435,14 @@ paths:
         name: request
         required: true
         schema:
-          $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.VerifyCredentialRequest'
+          $ref: '#/definitions/pkg_server_router.VerifyCredentialRequest'
       produces:
       - application/json
       responses:
         "200":
           description: OK
           schema:
-            $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.VerifyCredentialResponse'
+            $ref: '#/definitions/pkg_server_router.VerifyCredentialResponse'
         "400":
           description: Bad request
           schema:
@@ -1581,21 +1589,21 @@ paths:
     put:
       consumes:
       - application/json
-      description: Public Manifest to DWN
+      description: Publish Manifest to DWN
       parameters:
       - description: request body
         in: body
         name: request
         required: true
         schema:
-          $ref: '#/definitions/pkg_server_router.PublishManifestRequest'
+          $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.PublishManifestRequest'
       produces:
       - application/json
       responses:
         "201":
           description: Created
           schema:
-            $ref: '#/definitions/pkg_server_router.PublishManifestResponse'
+            $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.PublishManifestResponse'
         "400":
           description: Bad request
           schema:
@@ -1604,7 +1612,7 @@ paths:
           description: Internal server error
           schema:
             type: string
-      summary: Public Manifest to DWN
+      summary: Publish Manifest to DWN
       tags:
       - DWNAPI
   /v1/keys:
@@ -1618,7 +1626,7 @@ paths:
         name: request
         required: true
         schema:
-          $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.StoreKeyRequest'
+          $ref: '#/definitions/pkg_server_router.StoreKeyRequest'
       produces:
       - application/json
       responses:
@@ -1652,7 +1660,7 @@ paths:
         "200":
           description: OK
           schema:
-            $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetKeyDetailsResponse'
+            $ref: '#/definitions/pkg_server_router.GetKeyDetailsResponse'
         "400":
           description: Bad request
           schema:
@@ -1758,7 +1766,7 @@ paths:
     get:
       consumes:
       - application/json
-      description: Get manifest by id
+      description: Get a credential manifest by its id
       parameters:
       - description: ID
         in: path
@@ -1973,7 +1981,7 @@ paths:
         "200":
           description: OK
           schema:
-            $ref: '#/definitions/pkg_server_router.GetSchemasResponse'
+            $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetSchemasResponse'
         "500":
           description: Internal server error
           schema:
@@ -1991,14 +1999,14 @@ paths:
         name: request
         required: true
         schema:
-          $ref: '#/definitions/pkg_server_router.CreateSchemaRequest'
+          $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateSchemaRequest'
       produces:
       - application/json
       responses:
         "201":
           description: Created
           schema:
-            $ref: '#/definitions/pkg_server_router.CreateSchemaResponse'
+            $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateSchemaResponse'
         "400":
           description: Bad request
           schema:
@@ -2055,7 +2063,7 @@ paths:
         "200":
           description: OK
           schema:
-            $ref: '#/definitions/pkg_server_router.GetSchemaResponse'
+            $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetSchemaResponse'
         "400":
           description: Bad request
           schema:
@@ -2074,14 +2082,14 @@ paths:
         name: request
         required: true
         schema:
-          $ref: '#/definitions/pkg_server_router.VerifySchemaRequest'
+          $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.VerifySchemaRequest'
       produces:
       - application/json
       responses:
         "200":
           description: OK
           schema:
-            $ref: '#/definitions/pkg_server_router.VerifySchemaResponse'
+            $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.VerifySchemaResponse'
         "400":
           description: Bad request
           schema:
diff --git a/internal/credential/model.go b/internal/credential/model.go
index 99fcccf..ab24b0e 100644
--- a/internal/credential/model.go
+++ b/internal/credential/model.go
@@ -1,40 +1,110 @@
 package credential
 
 import (
+	"fmt"
+	"reflect"
+
 	"github.com/TBD54566975/ssi-sdk/credential"
 	"github.com/TBD54566975/ssi-sdk/credential/signing"
+	"github.com/goccy/go-json"
 	"github.com/pkg/errors"
 )
 
-// CredentialContainer acts as an abstraction over both possible credential representations
+// Container 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 {
+type Container struct {
 	// Credential ID
 	ID            string
 	Credential    *credential.VerifiableCredential
-	CredentialJWT *string
+	CredentialJWT *CredentialJWT
+}
+
+func (c Container) JWTString() string {
+	return string(*c.CredentialJWT)
+}
+
+type CredentialJWT string
+
+func (c *CredentialJWT) String() string {
+	return string(*c)
+}
+
+func CredentialJWTPtr(s string) *CredentialJWT {
+	return (*CredentialJWT)(&s)
 }
 
-func NewCredentialContainerFromJWT(credentialJWT string) (*CredentialContainer, error) {
+// NewCredentialContainerFromJWT attempts to parse a VC-JWT credential from a string into a Container
+func NewCredentialContainerFromJWT(credentialJWT string) (*Container, error) {
 	cred, err := signing.ParseVerifiableCredentialFromJWT(credentialJWT)
 	if err != nil {
 		return nil, errors.Wrap(err, "could not parse credential from JWT")
 	}
-	return &CredentialContainer{
+	return &Container{
 		ID:            cred.ID,
-		Credential:    cred,
-		CredentialJWT: &credentialJWT,
+		CredentialJWT: CredentialJWTPtr(credentialJWT),
 	}, nil
 }
 
-func (cc CredentialContainer) IsValid() bool {
+// NewCredentialContainerFromMap attempts to parse a data integrity credential from a piece of JSON,
+// which is represented as a map in go, into a Container
+func NewCredentialContainerFromMap(credMap map[string]interface{}) (*Container, error) {
+	var cred credential.VerifiableCredential
+	credMapBytes, err := json.Marshal(credMap)
+	if err != nil {
+		return nil, errors.Wrap(err, "could not marshal credential map")
+	}
+	if err = json.Unmarshal(credMapBytes, &cred); err != nil {
+		return nil, errors.Wrap(err, "could not unmarshal credential map")
+	}
+	container := Container{
+		ID:         cred.ID,
+		Credential: &cred,
+	}
+	if container.HasDataIntegrityCredential() {
+		return &container, nil
+	}
+	return nil, errors.New("credential does not have a data integrity proof")
+}
+
+func (cc Container) IsValid() bool {
 	return cc.ID != "" && (cc.HasDataIntegrityCredential() || cc.HasJWTCredential())
 }
 
-func (cc CredentialContainer) HasDataIntegrityCredential() bool {
+func (cc Container) HasSignedCredential() bool {
+	return cc.HasDataIntegrityCredential() || cc.HasJWTCredential()
+}
+
+func (cc Container) HasDataIntegrityCredential() bool {
 	return cc.Credential != nil && cc.Credential.Proof != nil
 }
 
-func (cc CredentialContainer) HasJWTCredential() bool {
+func (cc Container) HasJWTCredential() bool {
 	return cc.CredentialJWT != nil
 }
+
+// NewCredentialContainerFromArray attempts to parse arrays of credentials of any type (either data integrity or JWT)
+// into an array of CredentialContainers. The method will return an error if any of the credentials are invalid.
+func NewCredentialContainerFromArray(creds []interface{}) ([]Container, error) {
+	var containers []Container
+	for _, c := range creds {
+		switch c.(type) {
+		case string:
+			// JWT
+			container, err := NewCredentialContainerFromJWT(c.(string))
+			if err != nil {
+				return nil, errors.Wrap(err, "could not parse credential from JWT")
+			}
+			containers = append(containers, *container)
+		case map[string]interface{}:
+			// JSON
+			container, err := NewCredentialContainerFromMap(c.(map[string]interface{}))
+			if err != nil {
+				return nil, errors.Wrap(err, "could not parse credential from JSON")
+			}
+			containers = append(containers, *container)
+		default:
+			return nil, fmt.Errorf("invalid credential type: %s", reflect.TypeOf(c).Name())
+		}
+	}
+	return containers, nil
+}
diff --git a/internal/credential/verification.go b/internal/credential/verification.go
index baef87c..a0ee1fa 100644
--- a/internal/credential/verification.go
+++ b/internal/credential/verification.go
@@ -1,26 +1,34 @@
 package credential
 
 import (
+	"crypto"
+
 	credsdk "github.com/TBD54566975/ssi-sdk/credential"
 	"github.com/TBD54566975/ssi-sdk/credential/signing"
 	"github.com/TBD54566975/ssi-sdk/credential/verification"
 	didsdk "github.com/TBD54566975/ssi-sdk/did"
+	"github.com/goccy/go-json"
 	"github.com/pkg/errors"
 
 	"github.com/tbd54566975/ssi-service/internal/keyaccess"
+	"github.com/tbd54566975/ssi-service/internal/schema"
 	"github.com/tbd54566975/ssi-service/internal/util"
 )
 
-type CredentialVerifier struct {
-	verifier *verification.CredentialVerifier
-	resolver *didsdk.Resolver
+type Verifier struct {
+	verifier       *verification.CredentialVerifier
+	didResolver    *didsdk.Resolver
+	schemaResolver schema.Resolution
 }
 
 // NewCredentialVerifier creates a new credential verifier which executes both signature and static verification checks.
 // In the future the set of verification checks will be configurable.
-func NewCredentialVerifier(resolver *didsdk.Resolver) (*CredentialVerifier, error) {
-	if resolver == nil {
-		return nil, errors.New("resolver is nil")
+func NewCredentialVerifier(didResolver *didsdk.Resolver, schemaResolver schema.Resolution) (*Verifier, error) {
+	if didResolver == nil {
+		return nil, errors.New("didResolver cannot be nil")
+	}
+	if schemaResolver == nil {
+		return nil, errors.New("schemaResolver cannot be nil")
 	}
 	// TODO(gabe): consider making this configurable
 	verifiers := verification.KnownVerifiers
@@ -28,9 +36,10 @@ func NewCredentialVerifier(resolver *didsdk.Resolver) (*CredentialVerifier, erro
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to create static credential verifier")
 	}
-	return &CredentialVerifier{
-		verifier: verifier,
-		resolver: resolver,
+	return &Verifier{
+		verifier:       verifier,
+		didResolver:    didResolver,
+		schemaResolver: schemaResolver,
 	}, nil
 }
 
@@ -38,55 +47,98 @@ func NewCredentialVerifier(resolver *didsdk.Resolver) (*CredentialVerifier, erro
 
 // VerifyJWTCredential first parses and checks the signature on the given JWT credential. Next, it runs
 // a set of static verification checks on the credential as per the credential service's configuration.
-func (v CredentialVerifier) VerifyJWTCredential(token string) error {
-	cred, err := signing.ParseVerifiableCredentialFromJWT(token)
+func (v Verifier) VerifyJWTCredential(token CredentialJWT) error {
+	// first, parse the token to see if it contains a valid verifiable credential
+	cred, err := signing.ParseVerifiableCredentialFromJWT(token.String())
 	if err != nil {
 		return util.LoggingErrorMsg(err, "could not parse credential from JWT")
 	}
-	issuerDID := cred.Issuer.(string)
-	resolved, err := v.resolver.Resolve(issuerDID)
-	if err != nil {
-		return errors.Wrapf(err, "failed to resolve did: %s", issuerDID)
-	}
-	kid, pubKey, err := keyaccess.GetVerificationInformation(resolved.DIDDocument, "")
+
+	// resolve the issuer's key material
+	kid, pubKey, err := v.resolveIssuerKey(*cred)
 	if err != nil {
-		return util.LoggingErrorMsg(err, "could not get verification from credential")
+		return util.LoggingError(err)
 	}
+
+	// construct a signature verifier from the verification information
 	verifier, err := keyaccess.NewJWKKeyAccessVerifier(kid, pubKey)
 	if err != nil {
 		return util.LoggingErrorMsg(err, "could not create verifier")
 	}
-	if err := verifier.Verify(keyaccess.JWKToken{Token: token}); err != nil {
+
+	// verify the signature on the credential
+	if err := verifier.Verify(keyaccess.JWKToken{Token: token.String()}); err != nil {
 		return util.LoggingErrorMsg(err, "could not verify credential's signature")
 	}
-	if err = v.verifier.VerifyCredential(*cred); err != nil {
-		return util.LoggingErrorMsg(err, "static credential verification failed")
-	}
-	return err
+
+	return v.staticVerificationChecks(*cred)
 }
 
 // VerifyDataIntegrityCredential first checks the signature on the given data integrity credential. Next, it runs
 // a set of static verification checks on the credential as per the credential service's configuration.
-func (v CredentialVerifier) VerifyDataIntegrityCredential(credential credsdk.VerifiableCredential) error {
-	// TODO(gabe): perhaps this should be a verification method referenced on the proof object, not the issuer
-	issuerDID := credential.Issuer.(string)
-	resolved, err := v.resolver.Resolve(issuerDID)
+func (v Verifier) VerifyDataIntegrityCredential(credential credsdk.VerifiableCredential) error {
+	// resolve the issuer's key material
+	kid, pubKey, err := v.resolveIssuerKey(credential)
 	if err != nil {
-		return errors.Wrapf(err, "failed to resolve did: %s", issuerDID)
-	}
-	kid, pubKey, err := keyaccess.GetVerificationInformation(resolved.DIDDocument, "")
-	if err != nil {
-		return util.LoggingErrorMsg(err, "could not get verification information from credential")
+		return util.LoggingError(err)
 	}
+
+	// construct a signature verifier from the verification information
 	verifier, err := keyaccess.NewDataIntegrityKeyAccess(kid, pubKey)
 	if err != nil {
 		return util.LoggingErrorMsg(err, "could not create verifier")
 	}
+
+	// verify the signature on the credential
 	if err := verifier.Verify(&credential); err != nil {
 		return util.LoggingErrorMsg(err, "could not verify the credential's signature")
 	}
-	if err = v.verifier.VerifyCredential(credential); err != nil {
+
+	return v.staticVerificationChecks(credential)
+}
+
+// resolveIssuerKey resolves the issuer's public key from the credential's issuer DID.
+// TODO(gabe): perhaps this should be a verification method referenced on the proof object, not the issuer
+// TODO(gabe): support issuers that are not strings, but objects
+func (v Verifier) resolveIssuerKey(credential credsdk.VerifiableCredential) (kid string, pubKey crypto.PublicKey, err error) {
+	issuerDID := credential.Issuer.(string)
+	resolved, err := v.didResolver.Resolve(issuerDID, nil)
+	if err != nil {
+		err = errors.Wrapf(err, "failed to resolve did: %s", issuerDID)
+		return
+	}
+
+	// next, get the verification information (key) from the did document of the issuer
+	kid, pubKey, err = keyaccess.GetVerificationInformation(resolved.DIDDocument, "")
+	if err != nil {
+		err = errors.Wrapf(err, "failed to get verification information from the issuer's did document: %s", issuerDID)
+		return
+	}
+	return
+}
+
+// staticVerificationChecks runs a set of static verification checks on the credential as per the credential
+// service's configuration, such as checking the credential's schema, expiration, and object validity.
+func (v Verifier) staticVerificationChecks(credential credsdk.VerifiableCredential) error {
+	// if the credential has a schema, resolve it before it is to be used in verification
+	var verificationOpts []verification.VerificationOption
+	if credential.CredentialSchema != nil {
+		schemaID := credential.CredentialSchema.ID
+		resolvedSchema, err := v.schemaResolver.Resolve(schemaID)
+		if err != nil {
+			return errors.Wrapf(err, "for credential<%s> failed to resolve schemas: %s", credential.ID, schemaID)
+		}
+		schemaBytes, err := json.Marshal(resolvedSchema)
+		if err != nil {
+			return errors.Wrapf(err, "for credential<%s> failed to marshal schema: %s", credential.ID, schemaID)
+		}
+		verificationOpts = append(verificationOpts, verification.WithSchema(string(schemaBytes)))
+	}
+
+	// run the configured static checks on the credential
+	if err := v.verifier.VerifyCredential(credential, verificationOpts...); err != nil {
 		return util.LoggingErrorMsg(err, "static credential verification failed")
 	}
-	return err
+
+	return nil
 }
diff --git a/internal/schema/resolver.go b/internal/schema/resolver.go
new file mode 100644
index 0000000..058151f
--- /dev/null
+++ b/internal/schema/resolver.go
@@ -0,0 +1,10 @@
+package schema
+
+import (
+	"github.com/TBD54566975/ssi-sdk/credential/schema"
+)
+
+// Resolution is an interface that defines a generic method of resolving a schema
+type Resolution interface {
+	Resolve(id string) (*schema.VCJSONSchema, error)
+}
diff --git a/pkg/server/router/credential.go b/pkg/server/router/credential.go
index fe319a2..55d99c8 100644
--- a/pkg/server/router/credential.go
+++ b/pkg/server/router/credential.go
@@ -64,7 +64,7 @@ func (c CreateCredentialRequest) ToServiceRequest() credential.CreateCredentialR
 
 type CreateCredentialResponse struct {
 	Credential    *credsdk.VerifiableCredential `json:"credential,omitempty"`
-	CredentialJWT *string                       `json:"credentialJwt,omitempty"`
+	CredentialJWT *credmodel.CredentialJWT      `json:"credentialJwt,omitempty"`
 }
 
 // CreateCredential godoc
@@ -108,7 +108,7 @@ func (cr CredentialRouter) CreateCredential(ctx context.Context, w http.Response
 type GetCredentialResponse struct {
 	ID            string                        `json:"id"`
 	Credential    *credsdk.VerifiableCredential `json:"credential,omitempty"`
-	CredentialJWT *string                       `json:"credentialJwt,omitempty"`
+	CredentialJWT *credmodel.CredentialJWT      `json:"credentialJwt,omitempty"`
 }
 
 // GetCredential godoc
@@ -146,7 +146,7 @@ func (cr CredentialRouter) GetCredential(ctx context.Context, w http.ResponseWri
 
 type VerifyCredentialRequest struct {
 	DataIntegrityCredential *credsdk.VerifiableCredential `json:"credential,omitempty"`
-	CredentialJWT           *string                       `json:"credentialJwt,omitempty"`
+	CredentialJWT           *credmodel.CredentialJWT      `json:"credentialJwt,omitempty"`
 }
 
 func (vcr VerifyCredentialRequest) IsValid() bool {
@@ -198,7 +198,7 @@ func (cr CredentialRouter) VerifyCredential(ctx context.Context, w http.Response
 }
 
 type GetCredentialsResponse struct {
-	Credentials []credmodel.CredentialContainer `json:"credentials"`
+	Credentials []credmodel.Container `json:"credentials"`
 }
 
 // GetCredentials godoc
diff --git a/pkg/server/router/manifest.go b/pkg/server/router/manifest.go
index 9a1170d..a0a5285 100644
--- a/pkg/server/router/manifest.go
+++ b/pkg/server/router/manifest.go
@@ -7,6 +7,7 @@ import (
 
 	manifestsdk "github.com/TBD54566975/ssi-sdk/credential/manifest"
 
+	"github.com/tbd54566975/ssi-service/internal/credential"
 	"github.com/tbd54566975/ssi-service/pkg/service/manifest"
 
 	"github.com/pkg/errors"
@@ -86,7 +87,7 @@ type GetManifestResponse struct {
 
 // GetManifest godoc
 // @Summary      Get manifest
-// @Description  Get manifest by id
+// @Description  Get a credential manifest by its id
 // @Tags         ManifestAPI
 // @Accept       json
 // @Produce      json
@@ -178,16 +179,22 @@ 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
-	ApplicantDID string `json:"applicantDid" validate:"required"`
+	ApplicantDID string                            `json:"applicantDid" validate:"required"`
+	Application  manifestsdk.CredentialApplication `json:"credential_application" validate:"required"`
+	Credentials  []interface{}                     `json:"verifiableCredentials" validate:"required"`
 }
 
-func (sar SubmitApplicationRequest) ToServiceRequest() manifest.SubmitApplicationRequest {
-	return manifest.SubmitApplicationRequest{
-		Application:  sar.Application,
-		ApplicantDID: sar.ApplicantDID,
+func (sar SubmitApplicationRequest) ToServiceRequest() (*manifest.SubmitApplicationRequest, error) {
+	credContainer, err := credential.NewCredentialContainerFromArray(sar.Credentials)
+	if err != nil {
+		return nil, errors.Wrap(err, "could not parse submitted credentials")
 	}
+	return &manifest.SubmitApplicationRequest{
+		ApplicantDID: sar.ApplicantDID,
+		Application:  sar.Application,
+		Credentials:  credContainer,
+	}, nil
 }
 
 type SubmitApplicationResponse struct {
@@ -215,8 +222,14 @@ func (mr ManifestRouter) SubmitApplication(ctx context.Context, w http.ResponseW
 		return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest)
 	}
 
-	req := request.ToServiceRequest()
-	submitApplicationResponse, err := mr.service.ProcessApplicationSubmission(req)
+	req, err := request.ToServiceRequest()
+	if err != nil {
+		errMsg := "invalid submit application request"
+		logrus.WithError(err).Error(errMsg)
+		return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest)
+	}
+
+	submitApplicationResponse, err := mr.service.ProcessApplicationSubmission(*req)
 	if err != nil {
 		errMsg := "could not submit application"
 		logrus.WithError(err).Error(errMsg)
diff --git a/pkg/service/credential/credential.go b/pkg/service/credential/credential.go
index 01f3075..fd446cc 100644
--- a/pkg/service/credential/credential.go
+++ b/pkg/service/credential/credential.go
@@ -25,7 +25,7 @@ import (
 type Service struct {
 	storage  credstorage.Storage
 	config   config.CredentialServiceConfig
-	verifier *credint.CredentialVerifier
+	verifier *credint.Verifier
 
 	// external dependencies
 	keyStore *keystore.Service
@@ -63,13 +63,13 @@ func (s Service) Config() config.CredentialServiceConfig {
 	return s.config
 }
 
-func NewCredentialService(config config.CredentialServiceConfig, s storage.ServiceStorage, keyStore *keystore.Service, resolver *didint.Resolver, schema *schema.Service) (*Service, error) {
+func NewCredentialService(config config.CredentialServiceConfig, s storage.ServiceStorage, keyStore *keystore.Service, didResolver *didint.Resolver, schema *schema.Service) (*Service, error) {
 	credentialStorage, err := credstorage.NewCredentialStorage(s)
 	if err != nil {
 		errMsg := "could not instantiate storage for the credential service"
 		return nil, util.LoggingErrorMsg(err, errMsg)
 	}
-	verifier, err := credint.NewCredentialVerifier(resolver)
+	verifier, err := credint.NewCredentialVerifier(didResolver, schema)
 	if err != nil {
 		return nil, util.LoggingErrorMsg(err, "could not instantiate verifier for the credential service")
 	}
@@ -176,13 +176,13 @@ func (s Service) CreateCredential(request CreateCredentialRequest) (*CreateCrede
 	}
 
 	// store the credential
-	container := credint.CredentialContainer{
+	container := credint.Container{
 		ID:            cred.ID,
 		Credential:    cred,
-		CredentialJWT: credJWT,
+		CredentialJWT: credint.CredentialJWTPtr(*credJWT),
 	}
 	storageRequest := credstorage.StoreCredentialRequest{
-		CredentialContainer: container,
+		Container: container,
 	}
 	if err := s.storage.StoreCredential(storageRequest); err != nil {
 		errMsg := "could not store credential"
@@ -190,7 +190,7 @@ func (s Service) CreateCredential(request CreateCredentialRequest) (*CreateCrede
 	}
 
 	// return the result
-	response := CreateCredentialResponse{CredentialContainer: container}
+	response := CreateCredentialResponse{Container: container}
 	return &response, nil
 }
 
@@ -216,7 +216,7 @@ func (s Service) signCredentialJWT(issuer string, cred credential.VerifiableCred
 
 type VerifyCredentialRequest struct {
 	DataIntegrityCredential *credential.VerifiableCredential `json:"credential,omitempty"`
-	CredentialJWT           *string                          `json:"credentialJwt,omitempty"`
+	CredentialJWT           *credint.CredentialJWT           `json:"credentialJwt,omitempty"`
 }
 
 // IsValid checks if the request is valid, meaning there is at least one data integrity OR jwt credential, but not both
@@ -277,7 +277,7 @@ func (s Service) GetCredential(request GetCredentialRequest) (*GetCredentialResp
 		return nil, util.LoggingNewError(errMsg)
 	}
 	response := GetCredentialResponse{
-		credint.CredentialContainer{
+		credint.Container{
 			ID:            gotCred.CredentialID,
 			Credential:    gotCred.Credential,
 			CredentialJWT: gotCred.CredentialJWT,
@@ -296,12 +296,12 @@ func (s Service) GetCredentialsByIssuer(request GetCredentialByIssuerRequest) (*
 		return nil, util.LoggingErrorMsg(err, errMsg)
 	}
 
-	var creds []credint.CredentialContainer
+	var creds []credint.Container
 	for _, cred := range gotCreds {
-		container := credint.CredentialContainer{
+		container := credint.Container{
 			ID:            cred.CredentialID,
 			Credential:    cred.Credential,
-			CredentialJWT: cred.CredentialJWT,
+			CredentialJWT: (*credint.CredentialJWT)(cred.CredentialJWT),
 		}
 		creds = append(creds, container)
 	}
@@ -320,9 +320,9 @@ func (s Service) GetCredentialsBySubject(request GetCredentialBySubjectRequest)
 		return nil, util.LoggingErrorMsg(err, errMsg)
 	}
 
-	var creds []credint.CredentialContainer
+	var creds []credint.Container
 	for _, cred := range gotCreds {
-		container := credint.CredentialContainer{
+		container := credint.Container{
 			ID:            cred.CredentialID,
 			Credential:    cred.Credential,
 			CredentialJWT: cred.CredentialJWT,
@@ -343,9 +343,9 @@ func (s Service) GetCredentialsBySchema(request GetCredentialBySchemaRequest) (*
 		return nil, util.LoggingErrorMsg(err, errMsg)
 	}
 
-	var creds []credint.CredentialContainer
+	var creds []credint.Container
 	for _, cred := range gotCreds {
-		container := credint.CredentialContainer{
+		container := credint.Container{
 			ID:            cred.CredentialID,
 			Credential:    cred.Credential,
 			CredentialJWT: cred.CredentialJWT,
diff --git a/pkg/service/credential/model.go b/pkg/service/credential/model.go
index dfeac00..690cd0a 100644
--- a/pkg/service/credential/model.go
+++ b/pkg/service/credential/model.go
@@ -23,7 +23,7 @@ type CreateCredentialRequest struct {
 // 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.CredentialContainer
+	credential.Container
 }
 
 type GetCredentialRequest struct {
@@ -31,7 +31,7 @@ type GetCredentialRequest struct {
 }
 
 type GetCredentialResponse struct {
-	credential.CredentialContainer
+	credential.Container
 }
 
 type GetCredentialByIssuerRequest struct {
@@ -47,7 +47,7 @@ type GetCredentialBySchemaRequest struct {
 }
 
 type GetCredentialsResponse struct {
-	Credentials []credential.CredentialContainer
+	Credentials []credential.Container
 }
 
 type DeleteCredentialRequest struct {
diff --git a/pkg/service/credential/storage/bolt.go b/pkg/service/credential/storage/bolt.go
index 8ce2f48..e493d84 100644
--- a/pkg/service/credential/storage/bolt.go
+++ b/pkg/service/credential/storage/bolt.go
@@ -54,7 +54,7 @@ func buildStoredCredential(request StoreCredentialRequest) (*StoredCredential, e
 	// assume we have a Data Integrity credential
 	cred := request.Credential
 	if request.HasJWTCredential() {
-		parsedCred, err := signing.ParseVerifiableCredentialFromJWT(*request.CredentialJWT)
+		parsedCred, err := signing.ParseVerifiableCredentialFromJWT(request.CredentialJWT.String())
 		if err != nil {
 			return nil, errors.Wrap(err, "could not parse credential from jwt")
 		}
diff --git a/pkg/service/credential/storage/storage.go b/pkg/service/credential/storage/storage.go
index 24c976d..445da66 100644
--- a/pkg/service/credential/storage/storage.go
+++ b/pkg/service/credential/storage/storage.go
@@ -11,7 +11,7 @@ import (
 )
 
 type StoreCredentialRequest struct {
-	cred.CredentialContainer
+	cred.Container
 }
 
 type StoredCredential struct {
@@ -22,7 +22,7 @@ type StoredCredential struct {
 
 	// only one of these fields should be present
 	Credential    *credential.VerifiableCredential `json:"credential,omitempty"`
-	CredentialJWT *string                          `json:"token,omitempty"`
+	CredentialJWT *cred.CredentialJWT              `json:"token,omitempty"`
 
 	Issuer       string `json:"issuer"`
 	Subject      string `json:"subject"`
diff --git a/pkg/service/manifest/manifest.go b/pkg/service/manifest/manifest.go
index 45c1f9f..6b9ccc8 100644
--- a/pkg/service/manifest/manifest.go
+++ b/pkg/service/manifest/manifest.go
@@ -135,8 +135,9 @@ func (s Service) DeleteManifest(request DeleteManifestRequest) error {
 }
 
 func (s Service) ProcessApplicationSubmission(request SubmitApplicationRequest) (*SubmitApplicationResponse, error) {
-	credApp := request.Application
+	// TODO(gabe) validate the application's signature
 
+	credApp := request.Application
 	if err := credApp.IsValid(); err != nil {
 		return nil, util.LoggingErrorMsg(err, "application is not valid")
 	}
@@ -145,18 +146,42 @@ func (s Service) ProcessApplicationSubmission(request SubmitApplicationRequest)
 	if err != nil {
 		return nil, util.LoggingErrorMsg(err, "problem with retrieving manifest during application validation")
 	}
-
 	if gotManifest == nil {
-		return nil, util.LoggingNewError(fmt.Sprintf("application is not valid. A manifest does not exist with id: %s", credApp.ManifestID))
+		errMsg := fmt.Sprintf("application is not valid; a manifest does not exist with id: %s", credApp.ManifestID)
+		return nil, util.LoggingNewError(errMsg)
 	}
 
-	// validate
-	if err := isValidApplication(gotManifest, credApp); err != nil {
+	// validation
+
+	// first, validate that the application complies with the associated manifest
+	credentialManifest := gotManifest.Manifest
+	if err := manifest.IsValidCredentialApplicationForManifest(credentialManifest, credApp); err != nil {
 		return nil, util.LoggingErrorMsg(err, "could not validate application")
 	}
 
-	if err := manifest.IsValidCredentialApplicationForManifest(gotManifest.Manifest, credApp); err != nil {
-		return nil, util.LoggingErrorMsg(err, "could not validate application")
+	// next, validate that the credential(s) provided in the application are valid
+	// TODO(neal): this is coming in a future PR after a change to the SDK, and the below will be removed
+	if credentialManifest.PresentationDefinition != nil &&
+		len(credentialManifest.PresentationDefinition.InputDescriptors) > 0 &&
+		len(request.Credentials) == 0 {
+		errMsg := fmt.Sprintf("no credentials provided for application: %s against manifest: %s", credApp.ID, credentialManifest.ID)
+		return nil, util.LoggingNewError(errMsg)
+	}
+
+	// signature and validity checks
+	for _, credentialContainer := range request.Credentials {
+		verificationResult, err := s.credential.VerifyCredential(credential.VerifyCredentialRequest{
+			DataIntegrityCredential: credentialContainer.Credential,
+			CredentialJWT:           credentialContainer.CredentialJWT,
+		})
+		if err != nil {
+			errMsg := fmt.Sprintf("could not verify credential: %s", credentialContainer.Credential.ID)
+			return nil, util.LoggingErrorMsg(err, errMsg)
+		}
+		if !verificationResult.Verified {
+			errMsg := fmt.Sprintf("submitted credential<%s> is not valid: %s", credentialContainer.Credential.ID, verificationResult.Reason)
+			return nil, util.LoggingNewError(errMsg)
+		}
 	}
 
 	// store the application
@@ -178,10 +203,10 @@ func (s Service) ProcessApplicationSubmission(request SubmitApplicationRequest)
 		return nil, util.LoggingErrorMsg(err, "could not fulfill credential application: could not set application id")
 	}
 
-	var creds []cred.CredentialContainer
-	for _, od := range gotManifest.Manifest.OutputDescriptors {
+	var creds []cred.Container
+	for _, od := range credentialManifest.OutputDescriptors {
 		credentialRequest := credential.CreateCredentialRequest{
-			Issuer:     gotManifest.Manifest.Issuer.ID,
+			Issuer:     credentialManifest.Issuer.ID,
 			Subject:    request.ApplicantDID,
 			JSONSchema: od.Schema,
 			// TODO(gabe) need to add in data here to match the request + schema
@@ -193,7 +218,7 @@ func (s Service) ProcessApplicationSubmission(request SubmitApplicationRequest)
 			return nil, util.LoggingErrorMsg(err, "could not create credential")
 		}
 
-		creds = append(creds, credentialResponse.CredentialContainer)
+		creds = append(creds, credentialResponse.Container)
 	}
 
 	// build descriptor map based on credential type
@@ -214,7 +239,7 @@ func (s Service) ProcessApplicationSubmission(request SubmitApplicationRequest)
 	}
 
 	// set the information for the fulfilled credentials in the response
-	if err := responseBuilder.SetFulfillment(descriptors); err != nil {
+	if err = responseBuilder.SetFulfillment(descriptors); err != nil {
 		return nil, util.LoggingErrorMsg(err, "could not fulfill credential application: could not set fulfillment")
 	}
 	credRes, err := responseBuilder.Build()
@@ -236,28 +261,6 @@ func (s Service) ProcessApplicationSubmission(request SubmitApplicationRequest)
 	return &response, nil
 }
 
-func isValidApplication(gotManifest *manifeststorage.StoredManifest, application manifest.CredentialApplication) error {
-	if gotManifest == nil {
-		errMsg := fmt.Sprintf("application is not valid. A manifest does not exist with id: %s", application.ManifestID)
-		return util.LoggingNewError(errMsg)
-	}
-
-	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 application's presentation submissions descriptor map ids do not match the manifest's 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 79ddb86..deab998 100644
--- a/pkg/service/manifest/model.go
+++ b/pkg/service/manifest/model.go
@@ -35,13 +35,15 @@ type DeleteManifestRequest struct {
 // Application
 
 type SubmitApplicationRequest struct {
-	Application  manifestsdk.CredentialApplication
-	ApplicantDID string
+	ApplicantDID   string
+	Application    manifestsdk.CredentialApplication
+	Credentials    []cred.Container
+	ApplicationJWT string
 }
 
 type SubmitApplicationResponse struct {
 	Response   manifestsdk.CredentialResponse
-	Credential []cred.CredentialContainer
+	Credential []cred.Container
 }
 
 type GetApplicationRequest struct {
diff --git a/pkg/service/manifest/storage/bolt.go b/pkg/service/manifest/storage/bolt.go
index da60880..a5acdfe 100644
--- a/pkg/service/manifest/storage/bolt.go
+++ b/pkg/service/manifest/storage/bolt.go
@@ -2,9 +2,12 @@ package storage
 
 import (
 	"fmt"
+
 	"github.com/goccy/go-json"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
+
+	"github.com/tbd54566975/ssi-service/internal/util"
 	"github.com/tbd54566975/ssi-service/pkg/storage"
 )
 
@@ -87,8 +90,7 @@ func (b BoltManifestStorage) GetManifests() ([]StoredManifest, error) {
 func (b BoltManifestStorage) DeleteManifest(id string) error {
 	if err := b.db.Delete(manifestNamespace, id); err != nil {
 		errMsg := fmt.Sprintf("could not delete manifest: %s", id)
-		logrus.WithError(err).Error(errMsg)
-		return errors.Wrapf(err, errMsg)
+		return util.LoggingErrorMsg(err, errMsg)
 	}
 	return nil
 }
@@ -96,15 +98,12 @@ func (b BoltManifestStorage) DeleteManifest(id string) error {
 func (b BoltManifestStorage) StoreApplication(application StoredApplication) error {
 	id := application.Application.ID
 	if id == "" {
-		err := errors.New("could not store application without an ID")
-		logrus.WithError(err).Error()
-		return err
+		return util.LoggingNewError("could not store application without an ID")
 	}
 	applicationBytes, err := json.Marshal(application)
 	if err != nil {
 		errMsg := fmt.Sprintf("could not store application: %s", id)
-		logrus.WithError(err).Error(errMsg)
-		return errors.Wrapf(err, errMsg)
+		return util.LoggingErrorMsg(err, errMsg)
 	}
 	return b.db.Write(applicationNamespace, id, applicationBytes)
 }
@@ -117,15 +116,13 @@ func (b BoltManifestStorage) GetApplication(id string) (*StoredApplication, erro
 		return nil, errors.Wrapf(err, errMsg)
 	}
 	if len(applicationBytes) == 0 {
-		err := fmt.Errorf("application not found with id: %s", id)
-		logrus.WithError(err).Error("could not get application from storage")
-		return nil, err
+		err = fmt.Errorf("application not found with id: %s", id)
+		return nil, util.LoggingErrorMsg(err, "could not get application from storage")
 	}
 	var stored StoredApplication
-	if err := json.Unmarshal(applicationBytes, &stored); err != nil {
+	if err = json.Unmarshal(applicationBytes, &stored); err != nil {
 		errMsg := fmt.Sprintf("could not unmarshal stored application: %s", id)
-		logrus.WithError(err).Error(errMsg)
-		return nil, errors.Wrapf(err, errMsg)
+		return nil, util.LoggingErrorMsg(err, errMsg)
 	}
 	return &stored, nil
 }
@@ -134,19 +131,19 @@ func (b BoltManifestStorage) GetApplication(id string) (*StoredApplication, erro
 func (b BoltManifestStorage) GetApplications() ([]StoredApplication, error) {
 	gotApplications, err := b.db.ReadAll(applicationNamespace)
 	if err != nil {
-		errMsg := "could not get all applications"
-		logrus.WithError(err).Error(errMsg)
-		return nil, errors.Wrap(err, errMsg)
+		return nil, util.LoggingErrorMsg(err, "could not get all applications")
 	}
 	if len(gotApplications) == 0 {
 		logrus.Info("no applications to get")
 		return nil, nil
 	}
 	var stored []StoredApplication
-	for _, applicationBytes := range gotApplications {
+	for appKey, applicationBytes := range gotApplications {
 		var nextApplication StoredApplication
-		if err := json.Unmarshal(applicationBytes, &nextApplication); err == nil {
+		if err = json.Unmarshal(applicationBytes, &nextApplication); err == nil {
 			stored = append(stored, nextApplication)
+		} else {
+			logrus.WithError(err).Errorf("could not unmarshal stored application: %s", appKey)
 		}
 	}
 	return stored, nil
@@ -155,8 +152,7 @@ func (b BoltManifestStorage) GetApplications() ([]StoredApplication, error) {
 func (b BoltManifestStorage) DeleteApplication(id string) error {
 	if err := b.db.Delete(applicationNamespace, id); err != nil {
 		errMsg := fmt.Sprintf("could not delete application: %s", id)
-		logrus.WithError(err).Error(errMsg)
-		return errors.Wrapf(err, errMsg)
+		return util.LoggingErrorMsg(err, errMsg)
 	}
 	return nil
 }
@@ -164,15 +160,12 @@ func (b BoltManifestStorage) DeleteApplication(id string) error {
 func (b BoltManifestStorage) StoreResponse(response StoredResponse) error {
 	id := response.Response.ID
 	if id == "" {
-		err := errors.New("could not store response without an ID")
-		logrus.WithError(err).Error()
-		return err
+		return util.LoggingNewError("could not store response without an ID")
 	}
 	responseBytes, err := json.Marshal(response)
 	if err != nil {
 		errMsg := fmt.Sprintf("could not store response: %s", id)
-		logrus.WithError(err).Error(errMsg)
-		return errors.Wrapf(err, errMsg)
+		return util.LoggingErrorMsg(err, errMsg)
 	}
 	return b.db.Write(responseNamespace, id, responseBytes)
 }
@@ -181,19 +174,16 @@ func (b BoltManifestStorage) GetResponse(id string) (*StoredResponse, error) {
 	responseBytes, err := b.db.Read(responseNamespace, id)
 	if err != nil {
 		errMsg := fmt.Sprintf("could not get response: %s", id)
-		logrus.WithError(err).Error(errMsg)
-		return nil, errors.Wrapf(err, errMsg)
+		return nil, util.LoggingErrorMsg(err, errMsg)
 	}
 	if len(responseBytes) == 0 {
-		err := fmt.Errorf("response not found with id: %s", id)
-		logrus.WithError(err).Error("could not get response from storage")
-		return nil, err
+		err = fmt.Errorf("response not found with id: %s", id)
+		return nil, util.LoggingErrorMsg(err, "could not get response from storage")
 	}
 	var stored StoredResponse
-	if err := json.Unmarshal(responseBytes, &stored); err != nil {
+	if err = json.Unmarshal(responseBytes, &stored); err != nil {
 		errMsg := fmt.Sprintf("could not unmarshal stored response: %s", id)
-		logrus.WithError(err).Error(errMsg)
-		return nil, errors.Wrapf(err, errMsg)
+		return nil, util.LoggingErrorMsg(err, errMsg)
 	}
 	return &stored, nil
 }
@@ -203,18 +193,19 @@ func (b BoltManifestStorage) GetResponses() ([]StoredResponse, error) {
 	gotResponses, err := b.db.ReadAll(responseNamespace)
 	if err != nil {
 		errMsg := "could not get all responses"
-		logrus.WithError(err).Error(errMsg)
-		return nil, errors.Wrap(err, errMsg)
+		return nil, util.LoggingErrorMsg(err, errMsg)
 	}
 	if len(gotResponses) == 0 {
 		logrus.Info("no responses to get")
 		return nil, nil
 	}
 	var stored []StoredResponse
-	for _, responseBytes := range gotResponses {
+	for responseKey, responseBytes := range gotResponses {
 		var nextResponse StoredResponse
-		if err := json.Unmarshal(responseBytes, &nextResponse); err == nil {
+		if err = json.Unmarshal(responseBytes, &nextResponse); err == nil {
 			stored = append(stored, nextResponse)
+		} else {
+			logrus.WithError(err).Errorf("could not unmarshal stored response: %s", responseKey)
 		}
 	}
 	return stored, nil
@@ -223,8 +214,7 @@ func (b BoltManifestStorage) GetResponses() ([]StoredResponse, error) {
 func (b BoltManifestStorage) DeleteResponse(id string) error {
 	if err := b.db.Delete(responseNamespace, id); err != nil {
 		errMsg := fmt.Sprintf("could not delete response: %s", id)
-		logrus.WithError(err).Error(errMsg)
-		return errors.Wrapf(err, errMsg)
+		return util.LoggingErrorMsg(err, errMsg)
 	}
 	return nil
 }
diff --git a/pkg/service/manifest/storage/storage.go b/pkg/service/manifest/storage/storage.go
index 9554f72..0642703 100644
--- a/pkg/service/manifest/storage/storage.go
+++ b/pkg/service/manifest/storage/storage.go
@@ -5,6 +5,7 @@ import (
 
 	"github.com/TBD54566975/ssi-sdk/credential/manifest"
 
+	cred "github.com/tbd54566975/ssi-service/internal/credential"
 	"github.com/tbd54566975/ssi-service/internal/util"
 	"github.com/tbd54566975/ssi-service/pkg/storage"
 )
@@ -16,9 +17,11 @@ type StoredManifest struct {
 }
 
 type StoredApplication struct {
-	ID          string                         `json:"id"`
-	Application manifest.CredentialApplication `json:"application"`
-	ManifestID  string                         `json:"manifestId"`
+	ID             string                         `json:"id"`
+	ManifestID     string                         `json:"manifestId"`
+	Application    manifest.CredentialApplication `json:"application"`
+	Credentials    []cred.Container               `json:"credentials"`
+	ApplicationJWT string                         `json:"applicationJwt"`
 }
 
 type StoredResponse struct {
diff --git a/pkg/service/schema/model.go b/pkg/service/schema/model.go
index 7acd6ed..4a3c115 100644
--- a/pkg/service/schema/model.go
+++ b/pkg/service/schema/model.go
@@ -6,7 +6,7 @@ import (
 )
 
 const (
-	Version1 string = "1.0.0"
+	Version1 string = "1.0"
 )
 
 type CreateSchemaRequest struct {
diff --git a/pkg/service/schema/schema.go b/pkg/service/schema/schema.go
index 0ea6cc6..bfb7fe1 100644
--- a/pkg/service/schema/schema.go
+++ b/pkg/service/schema/schema.go
@@ -107,7 +107,7 @@ func (s Service) CreateSchema(request CreateSchemaRequest) (*CreateSchemaRespons
 		Name:     request.Name,
 		Author:   request.Author,
 		Authored: time.Now().Format(time.RFC3339),
-		Schema:   request.Schema,
+		Schema:   prepareJSONSchema(schemaID, request.Name, request.Schema),
 	}
 
 	storedSchema := schemastorage.StoredSchema{ID: schemaID, Schema: schemaValue}
@@ -128,6 +128,23 @@ func (s Service) CreateSchema(request CreateSchemaRequest) (*CreateSchemaRespons
 	return &CreateSchemaResponse{ID: schemaID, Schema: schemaValue, SchemaJWT: storedSchema.SchemaJWT}, nil
 }
 
+// make sure the schema is well-formed before proceeding
+func prepareJSONSchema(id, name string, s schema.JSONSchema) schema.JSONSchema {
+	if _, ok := s["$id"]; !ok {
+		s["$id"] = id
+	}
+	if _, ok := s["$schema"]; !ok {
+		s["$schema"] = "http://json-schema.org/draft-07/schema#"
+	}
+	if _, ok := s["description"]; !ok {
+		s["description"] = "schema for " + name
+	}
+	if _, ok := s["required"]; !ok {
+		s["required"] = []string{}
+	}
+	return s
+}
+
 // signSchemaJWT signs a schema after the key associated with the provided author for the schema as a JWT
 func (s Service) signSchemaJWT(author string, schema schema.VCJSONSchema) (*string, error) {
 	gotKey, err := s.keyStore.GetKey(keystore.GetKeyRequest{ID: author})
@@ -248,6 +265,14 @@ func (s Service) GetSchema(request GetSchemaRequest) (*GetSchemaResponse, error)
 	return &GetSchemaResponse{Schema: gotSchema.Schema, SchemaJWT: gotSchema.SchemaJWT}, nil
 }
 
+func (s Service) Resolve(id string) (*schema.VCJSONSchema, error) {
+	schemaResponse, err := s.GetSchema(GetSchemaRequest{ID: id})
+	if err != nil {
+		return nil, err
+	}
+	return &schemaResponse.Schema, nil
+}
+
 func (s Service) DeleteSchema(request DeleteSchemaRequest) error {
 
 	logrus.Debugf("deleting schema: %s", request.ID)


Test patch:
diff --git a/pkg/dwn/dwn_test.go b/pkg/dwn/dwn_test.go
index c110156..c5060ed 100644
--- a/pkg/dwn/dwn_test.go
+++ b/pkg/dwn/dwn_test.go
@@ -1,10 +1,11 @@
 package dwn
 
 import (
+	"testing"
+
 	"github.com/TBD54566975/ssi-sdk/credential/exchange"
 	manifestsdk "github.com/TBD54566975/ssi-sdk/credential/manifest"
 	"github.com/stretchr/testify/assert"
-	"testing"
 )
 
 func TestPublishManifest(t *testing.T) {
@@ -21,7 +22,7 @@ func getValidManifest() manifestsdk.CredentialManifest {
 
 	return manifestsdk.CredentialManifest{
 		ID:          "WA-DL-CLASS-A",
-		SpecVersion: "https://identity.foundation/credential-manifest/spec/v1.0.0/",
+		SpecVersion: manifestsdk.SpecVersion,
 		Issuer: manifestsdk.Issuer{
 			ID: "did:abc:123",
 		},
@@ -33,7 +34,7 @@ func getValidManifest() manifestsdk.CredentialManifest {
 					Constraints: &exchange.Constraints{
 						Fields: []exchange.Field{
 							{
-								Path: []string{".vc.id"},
+								Path: []string{".verifiableCredential.id"},
 							},
 						},
 					},
diff --git a/pkg/server/router/manifest_test.go b/pkg/server/router/manifest_test.go
index 4f70712..eaac273 100644
--- a/pkg/server/router/manifest_test.go
+++ b/pkg/server/router/manifest_test.go
@@ -12,6 +12,8 @@ import (
 	"github.com/stretchr/testify/assert"
 
 	"github.com/tbd54566975/ssi-service/config"
+	credmodel "github.com/tbd54566975/ssi-service/internal/credential"
+	"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/service/manifest"
@@ -84,16 +86,33 @@ func TestManifestRouter(t *testing.T) {
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, createdSchema)
 
-		// good manifest request
+		// issue a credential against the schema to the subject, from the issuer
+		createdCred, err := credentialService.CreateCredential(credential.CreateCredentialRequest{
+			Issuer:     issuerDID.DID.ID,
+			Subject:    applicantDID.DID.ID,
+			JSONSchema: createdSchema.ID,
+			Data:       map[string]interface{}{"licenseType": "WA-DL-CLASS-A"},
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, createdCred)
+
+		// good manifest request, which asks for a single verifiable credential in the VC-JWT format
 		createManifestRequest := getValidManifestRequest(issuerDID.DID.ID, createdSchema.ID)
 
 		createdManifest, err := manifestService.CreateManifest(createManifestRequest)
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, createdManifest)
-		assert.NotEmpty(tt, createdManifest.Manifest)
+
+		m := createdManifest.Manifest
+		assert.NotEmpty(tt, m)
 
 		// good application request
-		createApplicationRequest := getValidApplicationRequest(applicantDID.DID.ID, createdManifest.Manifest.ID, createManifestRequest.Manifest.PresentationDefinition.InputDescriptors[0].ID)
+		containers := []credmodel.Container{{
+			ID:            createdCred.ID,
+			CredentialJWT: createdCred.CredentialJWT,
+		}}
+		createApplicationRequest := getValidApplicationRequest(applicantDID.DID.ID, m.ID, m.PresentationDefinition.ID,
+			m.PresentationDefinition.InputDescriptors[0].ID, containers)
 
 		createdApplicationResponse, err := manifestService.ProcessApplicationSubmission(createApplicationRequest)
 		assert.NoError(tt, err)
@@ -103,11 +122,12 @@ func TestManifestRouter(t *testing.T) {
 	})
 }
 
+// getValidManifestRequest returns a valid manifest request, expecting a single JWT-VC EdDSA credential
 func getValidManifestRequest(issuerDID, schemaID string) manifest.CreateManifestRequest {
 	createManifestRequest := manifest.CreateManifestRequest{
 		Manifest: manifestsdk.CredentialManifest{
 			ID:          "WA-DL-CLASS-A",
-			SpecVersion: "https://identity.foundation/credential-manifest/spec/v1.0.0/",
+			SpecVersion: manifestsdk.SpecVersion,
 			Issuer: manifestsdk.Issuer{
 				ID: issuerDID,
 			},
@@ -119,10 +139,15 @@ func getValidManifestRequest(issuerDID, schemaID string) manifest.CreateManifest
 						Constraints: &exchange.Constraints{
 							Fields: []exchange.Field{
 								{
-									Path: []string{".vc.id"},
+									Path: []string{".verifiableCredential.id"},
 								},
 							},
 						},
+						Format: &exchange.ClaimFormat{
+							JWTVC: &exchange.JWTType{
+								Alg: []crypto.SignatureAlgorithm{crypto.EdDSA},
+							},
+						},
 					},
 				},
 			},
@@ -146,31 +171,31 @@ func getValidManifestRequest(issuerDID, schemaID string) manifest.CreateManifest
 	return createManifestRequest
 }
 
-func getValidApplicationRequest(applicantDID, manifestID, submissionDescriptorID string) manifest.SubmitApplicationRequest {
+func getValidApplicationRequest(applicantDID, manifestID, presDefID, submissionDescriptorID string, credentials []credmodel.Container) manifest.SubmitApplicationRequest {
 	createApplication := manifestsdk.CredentialApplication{
 		ID:          uuid.New().String(),
-		SpecVersion: "https://identity.foundation/credential-manifest/spec/v1.0.0/",
+		SpecVersion: manifestsdk.SpecVersion,
 		ManifestID:  manifestID,
 		Format: &exchange.ClaimFormat{
 			JWT: &exchange.JWTType{Alg: []crypto.SignatureAlgorithm{crypto.EdDSA}},
 		},
 		PresentationSubmission: &exchange.PresentationSubmission{
 			ID:           "psid",
-			DefinitionID: "id123",
+			DefinitionID: presDefID,
 			DescriptorMap: []exchange.SubmissionDescriptor{
 				{
 					ID:     submissionDescriptorID,
-					Format: "jwt",
+					Format: exchange.JWTVC.String(),
 					Path:   "$.verifiableCredential[0]",
 				},
 			},
 		},
 	}
 
-	createApplicationRequest := manifest.SubmitApplicationRequest{
-		Application:  createApplication,
+	// TODO(gabe) sign the request
+	return manifest.SubmitApplicationRequest{
 		ApplicantDID: applicantDID,
+		Application:  createApplication,
+		Credentials:  credentials,
 	}
-
-	return createApplicationRequest
 }
diff --git a/pkg/server/server_credential_test.go b/pkg/server/server_credential_test.go
index 9123126..0c7def2 100644
--- a/pkg/server/server_credential_test.go
+++ b/pkg/server/server_credential_test.go
@@ -11,11 +11,11 @@ import (
 	credsdk "github.com/TBD54566975/ssi-sdk/credential"
 	"github.com/TBD54566975/ssi-sdk/crypto"
 	didsdk "github.com/TBD54566975/ssi-sdk/did"
-	"github.com/TBD54566975/ssi-sdk/util"
 	"github.com/goccy/go-json"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
+	"github.com/tbd54566975/ssi-service/internal/credential"
 	"github.com/tbd54566975/ssi-service/pkg/server/router"
 	"github.com/tbd54566975/ssi-service/pkg/service/did"
 	"github.com/tbd54566975/ssi-service/pkg/service/schema"
@@ -610,7 +610,7 @@ func TestCredentialAPI(t *testing.T) {
 		assert.True(tt, verifyResp.Verified)
 
 		// bad credential
-		requestValue = newRequestValue(tt, router.VerifyCredentialRequest{CredentialJWT: util.StringPtr("badjwt")})
+		requestValue = newRequestValue(tt, router.VerifyCredentialRequest{CredentialJWT: credential.CredentialJWTPtr("bad")})
 		req = httptest.NewRequest(http.MethodPost, "https://ssi-service.com/v1/credentials/verification", requestValue)
 		err = credRouter.VerifyCredential(newRequestContext(), w, req)
 		assert.NoError(tt, err)
diff --git a/pkg/server/server_manifest_test.go b/pkg/server/server_manifest_test.go
index 81076dc..f4809a5 100644
--- a/pkg/server/server_manifest_test.go
+++ b/pkg/server/server_manifest_test.go
@@ -14,6 +14,7 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"github.com/tbd54566975/ssi-service/pkg/server/router"
+	"github.com/tbd54566975/ssi-service/pkg/service/credential"
 	"github.com/tbd54566975/ssi-service/pkg/service/did"
 	"github.com/tbd54566975/ssi-service/pkg/service/schema"
 	"github.com/tbd54566975/ssi-service/pkg/storage"
@@ -355,7 +356,13 @@ func TestManifestAPI(t *testing.T) {
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, issuerDID)
 
-		applicantDID := "did:example:abcd"
+		// create an applicant
+		applicantDID, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
+			Method:  didsdk.KeyMethod,
+			KeyType: crypto.Ed25519,
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, issuerDID)
 
 		// create a schema for the creds to be issued against
 		licenseSchema := map[string]interface{}{
@@ -371,6 +378,16 @@ func TestManifestAPI(t *testing.T) {
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, createdSchema)
 
+		// issue a credential against the schema to the subject, from the issuer
+		createdCred, err := credentialService.CreateCredential(credential.CreateCredentialRequest{
+			Issuer:     issuerDID.DID.ID,
+			Subject:    applicantDID.DID.ID,
+			JSONSchema: createdSchema.ID,
+			Data:       map[string]interface{}{"licenseType": "WA-DL-CLASS-A"},
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, createdCred)
+
 		// good request
 		createManifestRequest := getValidManifestRequest(issuerDID.DID.ID, createdSchema.ID)
 
@@ -383,11 +400,13 @@ func TestManifestAPI(t *testing.T) {
 		err = json.NewDecoder(w.Body).Decode(&resp)
 		assert.NoError(tt, err)
 
-		assert.NotEmpty(tt, resp.Manifest)
-		assert.Equal(tt, resp.Manifest.Issuer.ID, issuerDID.DID.ID)
+		m := resp.Manifest
+		assert.NotEmpty(tt, m)
+		assert.Equal(tt, m.Issuer.ID, issuerDID.DID.ID)
 
 		// good application request
-		createApplicationRequest := getValidApplicationRequest(applicantDID, resp.Manifest.ID, resp.Manifest.PresentationDefinition.InputDescriptors[0].ID)
+		createApplicationRequest := getValidApplicationRequest(applicantDID.DID.ID, m.ID, m.PresentationDefinition.ID,
+			m.PresentationDefinition.InputDescriptors[0].ID, []interface{}{createdCred.CredentialJWT})
 
 		applicationRequestValue := newRequestValue(tt, createApplicationRequest)
 		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests/applications", applicationRequestValue)
@@ -398,8 +417,7 @@ func TestManifestAPI(t *testing.T) {
 		assert.NoError(tt, err)
 
 		assert.NotEmpty(tt, appResp.Response)
-		assert.Equal(tt, appResp.Response.ManifestID, resp.Manifest.ID)
-
+		assert.Equal(tt, resp.Manifest.ID, appResp.Response.ManifestID)
 	})
 
 	t.Run("Test Get Application By ID and Get Applications", func(tt *testing.T) {
@@ -436,7 +454,13 @@ func TestManifestAPI(t *testing.T) {
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, issuerDID)
 
-		applicantDID := "did:example:abcd"
+		// create an applicant
+		applicantDID, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
+			Method:  didsdk.KeyMethod,
+			KeyType: crypto.Ed25519,
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, applicantDID)
 
 		// create a schema for the creds to be issued against
 		licenseSchema := map[string]interface{}{
@@ -452,6 +476,16 @@ func TestManifestAPI(t *testing.T) {
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, createdSchema)
 
+		// issue a credential against the schema to the subject, from the issuer
+		createdCred, err := credentialService.CreateCredential(credential.CreateCredentialRequest{
+			Issuer:     issuerDID.DID.ID,
+			Subject:    applicantDID.DID.ID,
+			JSONSchema: createdSchema.ID,
+			Data:       map[string]interface{}{"licenseType": "WA-DL-CLASS-A"},
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, createdCred)
+
 		// good request
 		createManifestRequest := getValidManifestRequest(issuerDID.DID.ID, createdSchema.ID)
 
@@ -464,8 +498,13 @@ func TestManifestAPI(t *testing.T) {
 		err = json.NewDecoder(w.Body).Decode(&resp)
 		assert.NoError(tt, err)
 
+		m := resp.Manifest
+		assert.NotEmpty(tt, m)
+		assert.Equal(tt, m.Issuer.ID, issuerDID.DID.ID)
+
 		// good application request
-		createApplicationRequest := getValidApplicationRequest(applicantDID, resp.Manifest.ID, resp.Manifest.PresentationDefinition.InputDescriptors[0].ID)
+		createApplicationRequest := getValidApplicationRequest(applicantDID.DID.ID, m.ID, m.PresentationDefinition.ID,
+			m.PresentationDefinition.InputDescriptors[0].ID, []interface{}{createdCred.CredentialJWT})
 
 		applicationRequestValue := newRequestValue(tt, createApplicationRequest)
 		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests/applications", applicationRequestValue)
@@ -536,6 +575,8 @@ func TestManifestAPI(t *testing.T) {
 		credentialService := testCredentialService(tt, bolt, keyStoreService, didService, schemaService)
 		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
 
+		w := httptest.NewRecorder()
+
 		// create an issuer
 		issuerDID, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
 			Method:  didsdk.KeyMethod,
@@ -544,7 +585,13 @@ func TestManifestAPI(t *testing.T) {
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, issuerDID)
 
-		applicantDID := "did:example:abcd"
+		// create an applicant
+		applicantDID, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
+			Method:  didsdk.KeyMethod,
+			KeyType: crypto.Ed25519,
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, issuerDID)
 
 		// create a schema for the creds to be issued against
 		licenseSchema := map[string]interface{}{
@@ -560,12 +607,21 @@ func TestManifestAPI(t *testing.T) {
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, createdSchema)
 
+		// issue a credential against the schema to the subject, from the issuer
+		createdCred, err := credentialService.CreateCredential(credential.CreateCredentialRequest{
+			Issuer:     issuerDID.DID.ID,
+			Subject:    applicantDID.DID.ID,
+			JSONSchema: createdSchema.ID,
+			Data:       map[string]interface{}{"licenseType": "WA-DL-CLASS-A"},
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, createdCred)
+
 		// good request
 		createManifestRequest := getValidManifestRequest(issuerDID.DID.ID, createdSchema.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)
 
@@ -573,8 +629,13 @@ func TestManifestAPI(t *testing.T) {
 		err = json.NewDecoder(w.Body).Decode(&resp)
 		assert.NoError(tt, err)
 
+		m := resp.Manifest
+		assert.NotEmpty(tt, m)
+		assert.Equal(tt, m.Issuer.ID, issuerDID.DID.ID)
+
 		// good application request
-		createApplicationRequest := getValidApplicationRequest(applicantDID, resp.Manifest.ID, resp.Manifest.PresentationDefinition.InputDescriptors[0].ID)
+		createApplicationRequest := getValidApplicationRequest(applicantDID.DID.ID, m.ID, m.PresentationDefinition.ID,
+			m.PresentationDefinition.InputDescriptors[0].ID, []interface{}{createdCred.CredentialJWT})
 
 		applicationRequestValue := newRequestValue(tt, createApplicationRequest)
 		req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/manifests/applications", applicationRequestValue)
diff --git a/pkg/server/server_schema_test.go b/pkg/server/server_schema_test.go
index 1f0e3c6..9e4d601 100644
--- a/pkg/server/server_schema_test.go
+++ b/pkg/server/server_schema_test.go
@@ -7,6 +7,7 @@ import (
 	"os"
 	"testing"
 
+	"github.com/TBD54566975/ssi-sdk/credential/schema"
 	"github.com/TBD54566975/ssi-sdk/crypto"
 	didsdk "github.com/TBD54566975/ssi-sdk/did"
 	"github.com/goccy/go-json"
@@ -33,16 +34,7 @@ func TestSchemaAPI(t *testing.T) {
 		didService := testDIDService(tt, bolt, keyStoreService)
 		schemaService := testSchemaRouter(tt, bolt, keyStoreService, didService)
 
-		simpleSchema := map[string]interface{}{
-			"type": "object",
-			"properties": map[string]interface{}{
-				"foo": map[string]interface{}{
-					"type": "string",
-				},
-			},
-			"required":             []interface{}{"foo"},
-			"additionalProperties": false,
-		}
+		simpleSchema := getTestSchema()
 		badSchemaRequest := router.CreateSchemaRequest{Schema: simpleSchema}
 		schemaRequestValue := newRequestValue(tt, badSchemaRequest)
 		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/schemas", schemaRequestValue)
@@ -83,20 +75,10 @@ func TestSchemaAPI(t *testing.T) {
 		didService := testDIDService(tt, bolt, keyStoreService)
 		schemaService := testSchemaRouter(tt, bolt, keyStoreService, didService)
 
-		simpleSchema := map[string]interface{}{
-			"type": "object",
-			"properties": map[string]interface{}{
-				"foo": map[string]interface{}{
-					"type": "string",
-				},
-			},
-			"required":             []interface{}{"foo"},
-			"additionalProperties": false,
-		}
-
 		w := httptest.NewRecorder()
 
 		// sign request with unknown DID
+		simpleSchema := getTestSchema()
 		schemaRequest := router.CreateSchemaRequest{Author: "did:test", Name: "test schema", Schema: simpleSchema, Sign: true}
 		schemaRequestValue := newRequestValue(tt, schemaRequest)
 		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/schemas", schemaRequestValue)
@@ -199,16 +181,8 @@ func TestSchemaAPI(t *testing.T) {
 		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,
-		}
+		simpleSchema := getTestSchema()
+
 		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)
@@ -273,16 +247,7 @@ func TestSchemaAPI(t *testing.T) {
 		assert.Contains(tt, err.Error(), "could not delete schema with id: bad")
 
 		// create a schema
-		simpleSchema := map[string]interface{}{
-			"type": "object",
-			"properties": map[string]interface{}{
-				"foo": map[string]interface{}{
-					"type": "string",
-				},
-			},
-			"required":             []interface{}{"foo"},
-			"additionalProperties": false,
-		}
+		simpleSchema := getTestSchema()
 
 		schemaRequest := router.CreateSchemaRequest{Author: "did:test", Name: "test schema", Schema: simpleSchema}
 		schemaRequestValue := newRequestValue(tt, schemaRequest)
@@ -320,3 +285,19 @@ func TestSchemaAPI(t *testing.T) {
 		assert.Contains(tt, err.Error(), "schema not found")
 	})
 }
+
+func getTestSchema() schema.JSONSchema {
+	return map[string]interface{}{
+		"$id":         "https://example.com/foo.schema.json",
+		"$schema":     "http://json-schema.org/draft-07/schema#",
+		"description": "foo schema",
+		"type":        "object",
+		"properties": map[string]interface{}{
+			"foo": map[string]interface{}{
+				"type": "string",
+			},
+		},
+		"required":             []interface{}{"foo"},
+		"additionalProperties": false,
+	}
+}
\ No newline at end of file
diff --git a/pkg/server/server_test.go b/pkg/server/server_test.go
index 84128ea..9c71604 100644
--- a/pkg/server/server_test.go
+++ b/pkg/server/server_test.go
@@ -121,7 +121,7 @@ func getValidManifestRequest(issuerDID, schemaID string) manifest.CreateManifest
 	createManifestRequest := manifest.CreateManifestRequest{
 		Manifest: manifestsdk.CredentialManifest{
 			ID:          "WA-DL-CLASS-A",
-			SpecVersion: "https://identity.foundation/credential-manifest/spec/v1.0.0/",
+			SpecVersion: manifestsdk.SpecVersion,
 			Issuer: manifestsdk.Issuer{
 				ID: issuerDID,
 			},
@@ -160,33 +160,33 @@ func getValidManifestRequest(issuerDID, schemaID string) manifest.CreateManifest
 	return createManifestRequest
 }
 
-func getValidApplicationRequest(applicantDID, manifestID, submissionDescriptorID string) manifest.SubmitApplicationRequest {
+func getValidApplicationRequest(applicantDID, manifestID, presDefID, submissionDescriptorID string, credentials []interface{}) router.SubmitApplicationRequest {
 	createApplication := manifestsdk.CredentialApplication{
 		ID:          uuid.New().String(),
-		SpecVersion: "https://identity.foundation/credential-manifest/spec/v1.0.0/",
+		SpecVersion: manifestsdk.SpecVersion,
 		ManifestID:  manifestID,
 		Format: &exchange.ClaimFormat{
 			JWT: &exchange.JWTType{Alg: []crypto.SignatureAlgorithm{crypto.EdDSA}},
 		},
 		PresentationSubmission: &exchange.PresentationSubmission{
 			ID:           "psid",
-			DefinitionID: "id123",
+			DefinitionID: presDefID,
 			DescriptorMap: []exchange.SubmissionDescriptor{
 				{
 					ID:     submissionDescriptorID,
-					Format: "jwt",
+					Format: exchange.JWTVC.String(),
 					Path:   "$.verifiableCredential[0]",
 				},
 			},
 		},
 	}
 
-	createApplicationRequest := manifest.SubmitApplicationRequest{
-		Application:  createApplication,
+	// TODO(gabe) sign the request
+	return router.SubmitApplicationRequest{
 		ApplicantDID: applicantDID,
+		Application:  createApplication,
+		Credentials:  credentials,
 	}
-
-	return createApplicationRequest
 }
 
 func testKeyStore(t *testing.T, bolt *storage.BoltDB) (*router.KeyStoreRouter, *keystore.Service) {