Step 1: Configure your study
A study is a few JSON files. The app reads them on your phone in Step 2, and you upload the same files to the CARP Portal when you go live.
How a study runs
Section titled “How a study runs”Every study follows the same flow on the participant’s phone. The files on this page decide what happens at each step.
-
Join. The participant joins with an email invitation (username and password) or, if the study allows it, anonymously with a QR code or a 5-character code. The app shows the invitation with the study’s purpose, from
protocol.json. -
Give consent. The participant reads the informed consent and signs that you may collect their data, from
consent.json. -
Allow permissions. The app asks for the permissions the protocol’s measures need, such as location or activity.
-
Collect. The app runs the protocol: each task starts when its trigger fires, its measures collect data, and the data goes to the protocol’s data endpoint.
-
Leave. The participant can leave the study at any time from their profile.
All texts along the way are shown in the phone’s language, from the translation files. See the screens in What participants see.
The files
Section titled “The files”| File | What it holds | Needed? | Section |
|---|---|---|---|
protocol.json |
What to collect, from which device, and when | Yes | Protocol |
consent.json |
The informed consent the participant reads and signs | No | Consent |
en.json, da.json, … |
The texts, one file per language | If you use keys | Translations |
To generate a file, run the Dart code in a test or a small script and save the result with toJsonString(...). Try it with the buttons below.
Protocol
Section titled “Protocol”The protocol answers four questions:
| Question | In the protocol | In this example |
|---|---|---|
| Where is data collected? | Device | The phone |
| What is collected? | Measures | Steps and battery |
| When is it collected? | Trigger | Right away, all the time |
| Where does data go? | Data endpoint | A database on the phone |
final protocol = SmartphoneStudyProtocol( name: 'My first study', // Texts are translation keys, see "Translations" below. studyDescription: StudyDescription( title: 'study.title', description: 'study.description', purpose: 'study.purpose', ), dataEndPoint: SQLiteDataEndPoint(),);
// The phone collects the data, the participant is the person using it.final phone = Smartphone();protocol.addPrimaryDevice(phone);protocol.addParticipantRole(ParticipantRole('Participant'));
// Start right away and keep collecting in the background.protocol.addTaskControl( ImmediateTrigger(), BackgroundTask(name: 'Steps and battery', measures: [ Measure(type: SensorSamplingPackage.STEP_EVENT), Measure(type: DeviceSamplingPackage.BATTERY_STATE), ]), phone,);Generated from the Dart code above. Don't edit this file by hand, change the Dart and generate it again.
{ "applicationData": { "protocolApiLevel": "2.0", "studyDescription": { "__type": "StudyDescription", "title": "study.title", "description": "study.description", "purpose": "study.purpose" }, "dataEndPoint": { "__type": "SQLiteDataEndPoint", "type": "SQLITE", "dataFormat": "dk.cachet.carp" } }, "id": "19dc6876-c094-4c54-8072-7936fbe438af", "createdOn": "2026-10-06T06:10:13.037500Z", "version": 0, "name": "My first study", "participantRoles": [ { "role": "Participant", "isOptional": false } ], "primaryDevices": [ { "__type": "dk.carp.cams.devices.Smartphone", "roleName": "Smartphone", "isOptional": false, "defaultSamplingConfiguration": {}, "isPrimaryDevice": true } ], "connectedDevices": [], "connections": [], "assignedDevices": {}, "tasks": [ { "__type": "dk.cachet.carp.common.application.tasks.MonitoringTask", "name": "Monitoring Smartphone", "measures": [ { "__type": "dk.cachet.carp.common.application.tasks.Measure.DataStream", "type": "dk.cachet.carp.error" }, { "__type": "dk.cachet.carp.common.application.tasks.Measure.DataStream", "type": "dk.cachet.carp.triggeredtask" }, { "__type": "dk.cachet.carp.common.application.tasks.Measure.DataStream", "type": "dk.cachet.carp.completedtask" } ] }, { "__type": "dk.cachet.carp.common.application.tasks.BackgroundTask", "name": "Steps and battery", "measures": [ { "__type": "dk.cachet.carp.common.application.tasks.Measure.DataStream", "type": "dk.cachet.carp.stepevent" }, { "__type": "dk.cachet.carp.common.application.tasks.Measure.DataStream", "type": "dk.cachet.carp.batterystate" } ] } ], "triggers": { "0": { "__type": "dk.cachet.carp.common.application.triggers.NoOpTrigger", "sourceDeviceRoleName": "Smartphone" }, "1": { "__type": "dk.cachet.carp.common.application.triggers.ImmediateTrigger", "sourceDeviceRoleName": "Smartphone" } }, "taskControls": [ { "triggerId": 0, "taskName": "Monitoring Smartphone", "destinationDeviceRoleName": "Smartphone", "control": "Start" }, { "triggerId": 1, "taskName": "Steps and battery", "destinationDeviceRoleName": "Smartphone", "control": "Start" } ], "expectedParticipantData": []}CAMS adds the Monitoring Smartphone task itself. It records errors and task events.
To collect more, add a measure from Available packages. To collect at other times, change the trigger. To ask the participant something, add a task. Everything a protocol can hold is in Study protocol.
Consent
Section titled “Consent”The informed consent is what a participant reads before the study starts. It is how you inform them: what the study is about, what data it collects, how their privacy is protected, what they will be asked to do, and that they can leave at any time. It can hold anything the participant should know before agreeing.
It is a short flow: the participant reads the sections, reviews them, signs with name and finger, and is done. Each section has a type (Overview, Data gathering, Privacy, Study tasks and more) that sets its icon and default title. See how it looks in the app in What participants see.
final document = RPConsentDocument(title: 'consent.title', sections: [ RPConsentSection( type: RPConsentSectionType.Overview, summary: 'consent.overview.summary', content: 'consent.overview.content', ), RPConsentSection( type: RPConsentSectionType.DataGathering, summary: 'consent.data.summary', content: 'consent.data.content', ),]);document.addSignature(RPConsentSignature(identifier: 'signature'));
// Read the sections, review and sign, done.final consent = RPOrderedTask(identifier: 'consent', steps: [ RPVisualConsentStep(identifier: 'visual', consentDocument: document), RPConsentReviewStep( identifier: 'review', title: 'consent.review.title', consentDocument: document, reasonForConsent: 'consent.review.reason', ), RPCompletionStep(identifier: 'done', title: 'consent.done.title', text: 'consent.done.text'),]);Generated from the Dart code above. Don't edit this file by hand, change the Dart and generate it again.
{ "__type": "RPOrderedTask", "identifier": "consent", "closeAfterFinished": true, "steps": [ { "__type": "RPVisualConsentStep", "identifier": "visual", "title": "", "optional": false, "consentDocument": { "__type": "RPConsentDocument", "signatures": [ { "__type": "RPConsentSignature", "identifier": "signature", "requiresName": true, "requiresSignatureImage": true } ], "title": "consent.title", "sections": [ { "__type": "RPConsentSection", "type": "Overview", "title": "title.overview", "summary": "consent.overview.summary", "content": "consent.overview.content" }, { "__type": "RPConsentSection", "type": "DataGathering", "title": "title.datagathering", "summary": "consent.data.summary", "content": "consent.data.content" } ] }, "askPermission": false }, { "__type": "RPConsentReviewStep", "identifier": "review", "title": "consent.review.title", "optional": false, "consentDocument": { "__type": "RPConsentDocument", "signatures": [ { "__type": "RPConsentSignature", "identifier": "signature", "requiresName": true, "requiresSignatureImage": true } ], "title": "consent.title", "sections": [ { "__type": "RPConsentSection", "type": "Overview", "title": "title.overview", "summary": "consent.overview.summary", "content": "consent.overview.content" }, { "__type": "RPConsentSection", "type": "DataGathering", "title": "title.datagathering", "summary": "consent.data.summary", "content": "consent.data.content" } ] }, "reasonForConsent": "consent.review.reason" }, { "__type": "RPCompletionStep", "identifier": "done", "title": "consent.done.title", "text": "consent.done.text", "optional": false } ]}No consent.json means there is nothing to sign, and the participant goes straight into the study.
Translations
Section titled “Translations”The protocol and consent don’t contain the texts. They contain keys, such as study.purpose. Each language has a file that maps the keys to the text, and the app shows the file that matches the phone’s language. en.json is used when there is no file for that language.
{ "study.title": "My first study", "study.description": "We count your steps for two weeks.", "study.purpose": "To learn how much people walk in a normal week.", "consent.title": "Informed consent", "consent.overview.summary": "This study counts your steps.", "consent.overview.content": "For two weeks, the app counts your steps in the background.", "consent.data.summary": "We collect steps and battery level.", "consent.data.content": "Nothing else is collected. You can leave at any time.", "consent.review.title": "Review", "consent.review.reason": "I have read the information and agree to take part.", "consent.done.title": "Thank you", "consent.done.text": "Your consent is saved."}{ "study.title": "Mit første studie", "study.description": "Vi tæller dine skridt i to uger.", "study.purpose": "At lære, hvor meget folk går i en almindelig uge.", "consent.title": "Informeret samtykke", "consent.overview.summary": "Dette studie tæller dine skridt.", "consent.overview.content": "I to uger tæller appen dine skridt i baggrunden.", "consent.data.summary": "Vi indsamler skridt og batteriniveau.", "consent.data.content": "Intet andet indsamles. Du kan til enhver tid trække dig.", "consent.review.title": "Gennemgang", "consent.review.reason": "Jeg har læst informationen og indvilliger i at deltage.", "consent.done.title": "Tak", "consent.done.text": "Dit samtykke er gemt."}{ "study.title": "Mi primer estudio", "study.description": "Contamos tus pasos durante dos semanas.", "study.purpose": "Conocer cuánto camina la gente en una semana normal.", "consent.title": "Consentimiento informado", "consent.overview.summary": "Este estudio cuenta tus pasos.", "consent.overview.content": "Durante dos semanas, la app cuenta tus pasos en segundo plano.", "consent.data.summary": "Recogemos los pasos y el nivel de batería.", "consent.data.content": "No se recoge nada más. Puedes abandonar el estudio en cualquier momento.", "consent.review.title": "Revisión", "consent.review.reason": "He leído la información y acepto participar.", "consent.done.title": "Gracias", "consent.done.text": "Tu consentimiento se ha guardado."}The three files have the same keys. Only the texts differ. Add as many languages as you need, one file each. Write en.json first, then let an AI tool translate it into the other languages, and have a native speaker check the result. Keep the keys unchanged, only the texts are translated.
Next: Step 2: Run your study