Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Table of Contents

Migration

The biggest change in the system will be that participant-facing APIs will no longer throw a 412 if the user is enrolled in any study in the app. This is different than the Subpopulation model where some subpopulations require consent, and some do not.

In the first version of consent, we matched subpopulations based on criteria, and then returned 412 if any of the required consents were not signed. To disable consent, you created a new, optional subpopulation, the only purpose of which was to turn off the 412 responses without having to sign consent.

In the new consent system, you would model this as two studies that shared the same protocols, and enrollment state, where one had a consent assigned to it and one did not. (There’s no use of criteria to select which one a user should enroll in…creating an external ID for an account is one way to enroll someone in a study.)

  1. Refactor subpopulations to use a single String guid, so an account’s consent signatures can continue to work to persist consents under the new consent system;

  2. For every required subpopulation, create a study, and change the subpopulation to enroll the user in that study when the subpopulation consent is signed. (Possibly require this going forward.)

  3. Migrate all existing accounts with consents by adding enrollment records for these.

  4. Going forward, consents will merge with the presence of an enrollment record, in many cases (if a study enrolled people but didn’t require them to consent or use an external ID, the reality is, we don’t know that they are enrolled).

The App object should include a consent version number, and existing studies should be marked as v1. Subpopulations, consents, and study consents should continue to work for v1 applications. The v2 consent system is described below.

Consent v2

Each study has one (and only one) required consent that must be signed to enroll in the study (with the caveat that the required consent may be in multiple languages). The consent can also We intend to revise our consent system for a few purposes:

  1. To provide a server model that can drive re-usable or dynamically-rendered user interfaces to design and to present informed consent;

  2. To better track where people fail to enroll (eligibility requirements, the informed consent process);

  3. Internally we are going to insert a layer between apps and consents in the form of a study. Consents will be reusable elements that are explicitly associated to studies.

In an alternative universe where we don’t intend to provide #1 (in particular), we could refactor Subpopulation to be this new top-level Consent object, and reuse almost all of our existing consent infrastructure with a new association between Study (originally Substudy) and Consent (originally Subpopulation). So it’s worth checking again that we really want to perform this refactor.

Consent v2

A study can be associated to zero or more consents, defined globally within an app context. Only one consent in a given language can be marked as the required consent. (Another alternative would be to push this down into the model and filter things like DocumentElements by language). The consent can be flagged if the client should force the user to reconsent (the user enrolled with a prior consent). There can be any number of optional, supplemental consents that can be presented to the user.

...

The consent includes a model for presenting the consent, and a model for verifying comprehension of the consent. The consent document will be assembled through the use of the content fields of all the document sections, with a signature block at the end of the consent which can be tailored for individual consents. Finally, any functionality we want to maintain from Subpopulations, like adding a data group to a participant when they consent, should be carried over to this new Consent record.

Code Block
languagejava
class Consent {
    // Consents are owned by organizations that have write permissions,
    // but other organizations can use the consent if needed.
    String ownerId;
    String name;
    String description;
    String language;
    // more than one consent can be marked required, 
    // but only if they all have different languages
    boolean required;
    boolean requiresReconsent;
    String appId;
    // if you sign this consent, you will be enrolled in this study.
    String studyId;
    String guid; 
    // subpopulation functionality maintained
    Set<String> dataGroupsAssignedWhileConsented
    // Name of the approving IRB
    String approvedBy;
    DateTime approvedOn;
    DateTime approvalExpiresOn;
    ComprehensionType type; // summative, formative
    List<DocumentSection> sections;
    // timestamps, deleted, version
}

class DocumentSection {
    int order;
    String title;
    String content; // markdown or HTML
    String summary; // markdown or HTML
    ComprehensionQuestion question; // optional
}

class ComprehensionQuestion {
    String question;
    List<Answer> answers;
}

class Answer {
    String text;
    boolean correct;
    // assuming it was either selected correctly, or in error, the
    // response will be an affirmation or a correction/further explanation
    String response; 
}

[TODO: Is this all just submitted in one graph, a little like Account, or will there be separate endpoints to work with these component parts?]

The enrollment record which indicates that a participant has been enrolled in a study, will include the GUID of the consent that was used when the participant consented. It may be the same or different as the current required consent guid. If it’s different and the current consent requiresReconsent, the app should act like the user has never consented and consent the user again (however, the user is consented and nothing will fail while this is happening, like pending uploads). If it’s not required, no action needs to be taken by the client. (Maybe we show somewhere that there’s a newer version of the consent document, I don’t know).

...

Study Consent APIs (devs and researchers)

These calls need to work with a StudyConsent associative record. This record will record if the consent is the required one for this study. (Because StudyConsent is already taken internal to the system, we should rename those first to ConsentDocuments).

Method

Path

Description

GET

/v5/studies/{studyId}/consents

Get all consents (possibly in summary) that are associated to the study. Only one consent of a given language can be required. This is available to all authenticated users.

POST

/v5/studies/{studyId}/consents/{guid}

Add a consent to a study

DELETE

/v5/studies/{studyId}/consents/{guid}

Remove a consent from a study

...

Method

Path

Description

GET

/v5/studies/{studyId}/consents/signatures

Get all signatures for this study

GET

/v5/studies/{studyId}/consents/{guid}/signature

Get the signature for a specific consent (is this needed though?)

POST

/v5/studies/{studyId}/consents/{guid}/signature

Sign this consent. If this is the required consent, enroll caller in study

DELETE

/v5/studies/{studyId}/consents/{guid}/signature

Withdraw consent. If this is the required consent, withdraw the user from the study (delete enrollment record).

POST

/v5/studies/{studyId}/consents/{guid}/signature/send

Send a copy of the consent to the caller via email or SMS. We propose to disable sending by default, as it is now rarely used.

DELETE

/v5/studies/{studyId}/consents/signatures

Withdraw this caller entirely from the study, deleting the enrollment record

Sign-In Workflow

This would be the new workflow around signing in to a Bridge app:

  1. Once authenticated, we would load enrollment records. If there is an enrollment record, the user is signed in with no further steps needed (session v2 is constructed and returned with a status of 200);

  2. If there are no enrollment records, we look at the apps consent version. In v1 we proceed as before. In v2, we return the same session as in step #1, but with a v1 session and a 412 response);

  3. The client should know what study the user should be enrolled in, but if they don’t, there is a list of studies API that can be used to present options to the user;

  4. The user goes through the consent for a study, and signs its required consent, adding an enrollment record to that user’s session;

  5. Calls to participant-facing endpoints no longer return 412 (consented == enrollments.length > 0).

Enrollments

Rather than loading and examining consent signatures to determine active enrollees in a study, there will be an enrollment record when an account is actively enrolled in a study. When consent is withdrawn, the enrollment record will be set a flag and behave like a logically deleted record. These records allow us to quickly determine the participant’s status, and they answer two questions for governance: how many people have we enrolled in the study, and how many have been withdrawn from the study? (Note that the questions I received from Vanessa also include “how many people have you removed from the study” which we’ll need to consider.)

Code Block
languagejava
class Enrollment {
  String studyId;
  // optional
  String externalId; 
  // the consent signed to enrollment
  String consentGuid; 
  // true if consentGuid != the study's required consent GUID,
  // and the required consent requires reconsent. User is 
  // still considered to be enrolled in study, e.g. uploads 
  // will not fail.
  boolean reconsentRequired;
  // behaves similarly to logical deletion
  boolean withdrawn;
}

...

Migration

The biggest change in the system will be that participant-facing APIs will no longer throw a 412 if the user is enrolled in any study in the app. This is different than the Subpopulation model where some subpopulations require consent, and some do not.

In the first version of consent, we matched subpopulations based on criteria, and then returned 412 if any of the required consents were not signed. To disable consent, you created a new, optional subpopulation, the only purpose of which was to turn off the 412 responses without having to sign consent.

In the new consent system, you would model this as two studies that shared the same protocols, and enrollment state, where one had a consent assigned to it and one did not. (There’s no use of criteria to select which one a user should enroll in…creating an external ID for an account is one way to enroll someone in a study.)

  1. Refactor subpopulations to use a single String guid, so an account’s consent signatures can continue to work to persist consents under the new consent system;

  2. For every required subpopulation, create a study, and change the subpopulation to enroll the user in that study when the subpopulation consent is signed. (Possibly require this going forward.)

  3. Migrate all existing accounts with consents by adding enrollment records for these.

  4. Going forward, consents will merge with the presence of an enrollment record, in many cases (if a study enrolled people but didn’t require them to consent or use an external ID, the reality is, we don’t know that they are enrolled).

The App object should include a consent version number, and existing studies should be marked as v1. Subpopulations, consents, and study consents should continue to work for v1 applications. The v2 consent system is described below.

These changes to consent lead to some significant changes to the user session. In v2 apps, I would like to clean up the session in the following ways:

...

And in place of many of these, I would add one field: enrollments. This would be an array of enrollment records without withdrawn records (see above). A user is considered “consented” to access participant-facing APIs without receiving a 412 from the API if they have at least one enrollment record.

SQL

The new consent objects will be created in SQL and will replace StudyConsent, Subpopulation. ConsentSignature will continue to be stored in SQL as it is now, repurposing the AccountConsents table (the content of a signature is not changing).

The consent signatures table may be able to store signatures for the v2 version of consent, if the consent GUID can be stored in the subpopulation GUID column (the values are in fact globally unique).

Candidates API

Code Block
Candidate {
    String anonymousId;
    DateTime firstContactOn;
    DateTime mostRecentContactOn;
    String anonSessionToken;
    // should map 1:1 with a consent, but we can include consentGuid
    String studyId; 
    String userId;
    String stepCompleted; // eligibility, comprehension, consented, demographics
    Enum eligibility; // eligible, ineligible
    List<RejectedAnswer> eligibilityRejections;
    JsonNode clientState;
}

...