Integration Documentation
Getting Started
Diet ID provides digital nutrition tools to assess and track current nutritional intake, develop custom nutrition plans based on health goals, and guide individuals to their goals through personalized digital daily coaching. As a partner, you can use our stand-alone web-based tools with your population.
If you use another digital platform to manage your population and want to embed Diet ID’s modules there, Diet ID can support integration to help you build a seamless experience.
Diet ID has 3 different modules: An assessment module, a goal setting module, and a daily actions module. Depending on your needs, the Diet ID team will configure your instance with the right combination of modules. Our team will also customize your instance of Diet ID with the correct branding, color palette, copy, custom questions, and custom content display. Your custom instance of Diet ID will be accessible via https://your-subdomain.dietid.com. A unique subdomain can be provided by the Diet ID team.
There are a few ways to integrate Diet ID into your experience. They generally address two concerns: how the user interacts with Diet ID’s modules and how you retrieve the results of those interactions.
1. Retrieving Results
To retrieve results in real time, data can be manually accessed by you, pushed to you via webhooks, or pulled by you via our API. Depending on your relationship with your users, you may have different types of access to data. For example, if you are a HIPAA compliant health care provider, you are eligible to access our full data set.
1.1 Use our Admin Portal (no technical integration required)
If you want to manually access you users’ diet assessment data and goal setting results, we can give you access to an admin portal where you’ll receive user results in real time. Data can also be exported in CSV and JSON directly from that reporting tool. Using this admin portal does not require any additional technical integration.
This is the simplest and fastest way to access your population’s data. Even if you have your own patient or client management system like an EHR, many of our partners choose this solution because you can get up and running in as little as 1 day.
1.2 Webhook (some integration required)
If you want to automatically receive a user’s data such that it is appended to your own patient or client management system, we can set up a webhook with your system. Whenever a user completes a Diet ID assessment, their results will be POST'ed to the configured URL you share with us. If users are created via SSO, the data we share via the webhook will include the user id you passed to us for easy mapping with existing users.
For an example of what's sent, please see the Webhook with PHI or Webhook without PHI examples, depending on whether your organization would like to receive PHI.
For data definitions, please see the Typings and Definitions sections below.
2. Choose your Integration
To handle user interactions, you can point the user to a white-labeled Diet ID page, or embed Diet ID within your app.
2.1 Direct URL
The simplest way to add Diet ID is to distribute the url for your custom, white-labeled product (e.g., https://your-subdomain.dietid.com or a custom domain) via your website, app, email, or other means.
The experience is fully branded with your name, logo, and colors. When users visit the site they will create an account, go through the process, and any results can be manually sent to you.
Setup for this usually takes less than a day.
2.2 Embedded
If you have an app or a website and you want Diet ID’s modules accessible there, you can embed Diet ID’s modules with a simple iframe. To do this simply add an iframe tag with the src attribute set to your custom url.
If you’d like to hide certain elements of the Diet ID modules in your iframe, you can add parameters to the URL:
Users will still need to create accounts or sign in to use the tools, but the experience will take place within your app or digital platform, and therefore appear more seamless.
This method is perfect if you have a website or landing page where you drive all of your users, and you want Diet ID’s modules to “live” there, but you have limited tech resources.
Set up for this usually takes one day.
2.3 Embedded with SSO
If you want your users to use Diet ID without having to create accounts, we can automatically create accounts for them via SSO. This allows for seamless switching between your app and Diet ID, and ensures a one-to-one mapping between your user accounts and corresponding Diet ID accounts.
Getting this running will require some backend work, as you'll be generating SSO urls using a secret key shared between your backend and Diet ID's backend. See getWorkflowUrl
in our example client for how to generate the SSO url server-side.
Once the url is generated and sent to the frontend, simply set the src
attribute of an iframe to the SSO url and you'll be good to go. Users who haven't completed an assessment will see the assessment, while existing users will see their results. Accounts are automatically deduped based on the user id you pass to Diet ID.
2.4 API
If you want to pull data as needed, we have a public API available! Please see the API Explorer for further information on the following endpoints:
Example Endpoint:
- https://api-staging.dietid.com/api/v3/diets - Retrieve list of Diet Types
API Environments:
- Staging API: https://api-staging.dietid.com/api/{version}/{endpoint}
- Production API: https://api-prod.dietid.com/api/{version}/{endpoint} - US Deployment
- Production API: https://api-prod.dietid.ca/api/{version}/{endpoint} - Canada Deployment
Example Requests:
curl -X POST https://api-prod.dietid.com/api/v3/sso
-H 'Content-Type: application/json'
-H "Accept: application/json"
-H "Authorization: Bearer {clientSecret}"
-H "X-DietID-PartnerUserId: abc123"
-H "X-DietID-ClientId: {clientToken}"
-d '{
"partnerUserId": "abc123",
"email": "example@dietid.com",
"firstName": "NameTest",
"lastName": "LastNameTest",
"healthGoals" : ["ControlBloodPressure"],
"additionalParams": { "hello": "world" },
"patientAlert": "This is a provider provided alert",
"height": 85,
"weight": 123,
"age": 45,
"parentalConsentGiven": true,
"DOB": "1999-11-12",
"postalCode": "90210",
"gender": "Male",
"desiredUsername": "desired_username_123",
"activityLevel": "HighlyActive"
}'
curl https://api-staging.dietid.com/api/v3/diets
-H "Accept: application/json"
-H "Authorization: Bearer {userToken || clientSecret}"
Endpoints
POST '/api/v1/sso'
GET '/api/v1/firehose/users/partnerUserId/:partner_user_id'
GET '/api/v1/firehose/users/:user_id'
Beta Endpoints (Subject to change)
POST '/api/v3/sso'
GET '/api/v3/goals'
GET '/api/v3/goals/all'
GET '/api/v3/goals/:slug'
GET '/api/v3/diets'
GET '/api/v3/diets/all'
GET '/api/v3/diets/:diet_code'
GET '/api/v3/diets/:diet_code/:quality'
GET '/api/v3/diets/compare/:diet_code/:quality/:ideal_code/:ideal_quality'
API Rate Limits
- 10 queries per second (QPS) per IP address.
- Rate limit of 1,000 requests per hour and per user. Diet ID reserves the right to enforce API Rate-Limit’s at our discretion.
- Service may be affected if you are over limit for 24 consecutive hours.
3. Customize your Integration
The following URL paramters can be included to hide specific features. Multiple parameters can also be included to hide multiple features, for example:
'?view=nosidebar'
'?view=nosidebar,noprogressbar'
The following ptional parameters are also available:
nosidebar
- Hide the sidebarnoprogressbar
- Hide the progress barnolanding
- Hide the landing screennoonboarding
- Hide the onboarding screennohelp
- Hide the help featurenochat
- Hide the chat featurenofeedback
- Hide the feedback featurenofoodgroups
- Omit food group datanocalories
- Omit calorie datanodetails
- Hide additional detailsnoworkflowresults
- Omit workflow results
Example API Integration Flows
Frontend
frontend is an example React frontend that demos how to use the API Client to interact with Diet ID and handle the different states the system may go through.
API Client
api-client is an example frontend library that simplifies and documents interactions with the Diet ID API. It communicates with Diet ID via the API Gateway.
API Gateway (SSO)
api-gateway is a backend script that authenticates and proxies requests from the API Client to Diet ID.
Webhook Typings
import { Opaque as _Opaque } from 'type-fest'
export type Opaque<Type, Token = unknown> = _Opaque<Type, Token>
/**
* Screener questions that are not shown to the user are sent as null.
*/
export type ScreenerResponse = 'Yes' | 'No' | 'Sometimes' | null
/**
* If during the screener the user indicates that they consume meat and grains
* then they are also asked if they follow a particular style of diet. Otherwise this value is null.
* The string type is only necessary for when the user specifies a style other than what's listed.
* Free text responses to "Other ethnic style" and "Other pattern" are sent as "OtherEthnic:foo" and
* "OtherPattern:foo" respectively where foo is what the user typed in the textbox.
*/
export type StyleScreenerResponse = 'None' | 'LOC' | 'LOF' | 'MED' | 'MEX' | 'SOU' | string | null
export type Gender = 'Male' | 'Female' | 'NonBinary' | 'PreferToSelfDescribe' | 'PreferNotToAnswer'
export type WeightTrend = 'Rising' | 'Constant' | 'Falling'
export type ActivityLevel = 'Sedentary' | 'BelowGuidelines' | 'MeetsGuidelines' | 'AboveGuidelines' | 'HighlyActive'
export type DietRestriction =
| 'DairyFree'
| 'NutFree'
| 'GlutenFree'
| 'WheatFree'
| 'ShellfishFree'
| 'SoyFree'
| 'PeanutFree'
| 'AlcoholFree'
| 'EggFree'
| 'Halal'
| 'Kosher'
| 'Organic'
/**
* The string type is only necessary for when the user specifies a goal other than what's listed.
* "Other" goals are sent as "other:foo" where foo is what the user typed in the textbox.
*/
export type HealthGoal =
| 'ReduceCardioRisk'
| 'ControlBloodPressure'
| 'PreventOrControlDiabetes'
| 'ManageHeartFailure'
| 'DecreaseInflammation'
| 'ImproveOverall'
| 'ManageWeight'
| 'ManageHighCholesterol'
| 'ManageFoodSensitivities'
| 'ImproveGutHealth'
| string
export type WeightGoal = 'Lose' | 'Maintain' | 'Gain'
export type NutrientUnit = 'g' | 'mg' | 'mcg' | '%' | 'kcal'
Model Typings
Challenge
import { Opaque } from '../types'
export interface Challenge {
id: Opaque<number, 'Challenge'>
/**
* The name of the challenge.
*/
name: string
}
Challenge Subscription
import { ChallengeSubscriptionState, Opaque } from '../types'
import { Challenge } from './Challenge'
import { Checkin } from './Checkin'
import { Tip } from './Tip'
import { User } from './User'
export interface ChallengeSubscription {
id: Opaque<number, 'ChallengeSubscription'>
/**
* The user the challenge subscription belongs to.
*/
userId: User['id']
/**
* The challenge the challenge subscription belongs to.
*/
challengeId: Challenge['id']
/**
* @todo Add description
*/
day: any
/**
* A helpful tip for the user to complete the challenge.
*/
tip: Tip | null
/**
* The number of times the user should check in throughout the day.
* For example, a "drink 8 glasses of water" challenge may be 8.
*/
checkinsPerDay: number
/**
* The challenge subscription's state. Valid transitions are:
*
* StartingTomorrow -> Active
*
* Active -> MissedYesterday | Failed | Succeeded
*
* Failed -> StartingTomorrow | Active
*/
state: ChallengeSubscriptionState
/**
* Past checkins the user has made for the challenge subscription.
*/
checkins: Checkin[]
}
Checkin
import { Opaque } from '../types'
import { ChallengeSubscription } from './ChallengeSubscription'
export interface Checkin {
id: Opaque<number, 'Checkin'>
/**
* The challenge subscription this checkin is for.
*/
challengeSubscriptionId: ChallengeSubscription['id']
/**
* Arbitrary additional data passed to Diet ID when the user checked in.
*/
data: object | null
}
Diet
import { Opaque } from '../types'
export interface Diet {
id: Opaque<number, 'Diet'>
/**
* Human-readable label for the diet type (e.g., "American").
*/
name: string
/**
* Unique code for the diet type (e.g., "AME")
*/
code: string
/**
* The quality of the diet on a scale of 1 to 10 (e.g., 5).
*/
quality: number
/**
* Human-readable label for the diet type and quality (e.g., "American quality 7 (7 out of 10)")
*/
longName: string
/**
* A short sentence describing the diet (e.g., "American Quality 5 (Q5) is characterized by fresh and processed meat, ...")
*/
description: string
/**
* The Healthy Eating Index (HEI) of the diet.
*/
hei: number
/**
* A photo of the diet's typical food and drink.
*/
fingerprintPhotoUrl: string
/**
* @todo Add description
*/
foodGroupValues: { [foodGroupId: string]: number }
/**
* Url to a PDF of a mealplan
*/
mealPlanUrl: string
}
Nutrition Info
import { Opaque } from '../types'
import { NutrientUnit } from '../types'
export interface NutritionInfo {
/**
* Unique identifier (e.g., "added-sugars-by-total-sugars")
*/
key: Opaque<string, 'NutritionInfo'>
/**
* Quantitative value (e.g., 60.06351427805127)
*/
value: number
/**
* Value's unit of measurement (e.g., "g")
*/
unit: NutrientUnit
/**
* Human-readable label for display of the value and unit (e.g., "Added Sugars")
*/
label: string
/**
* @todo Add description
*/
isDefault: boolean
}
Tip
import { Opaque } from '../types'
export interface Tip {
id: Opaque<number, 'Tip'>
/**
* The content of the tip.
*/
content: string
}
User
import { Opaque } from '../types'
import { Challenge } from './Challenge'
import { ChallengeSubscription } from './ChallengeSubscription'
import { Workflow } from './Workflow'
/**
* A user identifier provided by you, the partner
*/
export type PartnerUserID = Opaque<string, 'PartnerUserID'>
export interface User {
/**
* The user's unique identifier within Diet ID's system (assigned by Diet ID).
*/
id: Opaque<number, 'User'>
/**
* The user's unique identifier within a partner system (assigned by you).
*/
partnerUserId: PartnerUserID | null
/**
* The record of the assessment the user took to determine their ID diet.
*/
dietIdWorkflowId: Workflow['id'] | null
/**
* The record of the assessment the user took to determine their Ideal diet.
*/
dietIdealWorkflowId: Workflow['id'] | null
/**
* The challenge the user is currently participating in.
*/
challenge: Challenge | null
/**
* The join record between the user and their current challenge, which contains additional info specific to that user.
*/
challengeSubscription: ChallengeSubscription | null
}
Workflow
import {
ActivityLevel,
DietRestriction,
Gender,
HealthGoal,
Opaque,
ScreenerResponse,
StyleScreenerResponse,
WeightGoal,
WeightTrend
} from '../types'
import { Challenge } from './Challenge'
import { Diet } from './Diet'
import { NutritionInfo } from './NutritionInfo'
import { User } from './User'
/**
* A workflow identifier provided by you, the partner
*/
export type PartnerWorkflowID = Opaque<string, 'PartnerWorkflowID'>
export interface Workflow {
id: Opaque<number, 'Workflow'>
/**
* The workflow's unique identifier within a partner system (assigned by you).
*/
partnerWorkflowId: PartnerWorkflowID | null
/**
* The user the workflow belongs to.
*/
userId: User['id']
/**
* A private url to view the workflow results.
*/
shareUrl: string
/**
* Basic demographic and lifestyle info
*/
userInfo?: {
gender: Gender
ageInYears: number
weightInPounds: number
heightInInches: number
weightTrend: WeightTrend
activityLevel: ActivityLevel
}
/**
* Diet screeners describe what a user currently does and does not eat.
* Note: Many of these will be null since we ask the user one screener question
* at a time and often skip a number of them based on branching logic.
*/
dietScreener: {
meat: ScreenerResponse
poultry: ScreenerResponse
fish: ScreenerResponse
grains: ScreenerResponse
dairy: ScreenerResponse
style: StyleScreenerResponse
}
/**
* Diet restrictions are what a user cannot eat due to allergies, religious observance, etc.
*/
dietRestrictions: DietRestriction[]
/**
* Ideal goals are selected by the user to help determine what ideal diets are optimal for their needs.
*/
idealGoals: {
/**
* Health goals are specific things the user wants to accomplish with their change in diet.
*/
forHealth: HealthGoal[]
/**
* A weight goal qualifies the "ManageWeight" health goal (whether to lose, maintain, or gain weight)
*/
weightGoal: WeightGoal | null
/**
* The goal diet is a diet the user specifically wants to move towards apart from any health goals.
*/
goalDiet: Diet['code'] | null
}
/**
* The user's ID diet as determined by the assessment.
*/
dietId: Diet | null
/**
* The user's Ideal diet as determined by the assessment.
*/
dietIdeal: Diet | null
/**
* Nutrition info for the user's ID diet.
* This is workflow-specific data, with baseline diet nutrients adjusted for height, weight, etc.
*/
dietIdNutritionInfo: NutritionInfo[] | null
/**
* Nutrition info for the user's Ideal diet.
* This is workflow-specific data, with baseline diet nutrients adjusted for height, weight, etc.
*/
dietIdealNutritionInfo: NutritionInfo[] | null
/**
* The sequence of challenges a user is pre-determined to complete based on their ID and Ideal diets.
*/
challengeIds?: Challenge['id'][]
/**
* Whether the workflow has been completed or is a work in progress.
*/
completed: boolean
/**
* The UTC datetime when the workflow was started.
*/
createdAt: Date
}
Index
export * from './Challenge'
export * from './ChallengeSubscription'
export * from './Checkin'
export * from './Diet'
export * from './NutritionInfo'
export * from './Tip'
export * from './User'
export * from './Workflow'
Endpoint Definitions
createChallengeSubscription
import { request } from './util'
import { Challenge, ChallengeSubscription } from '../models'
export interface CreateChallengeSubscriptionBody {
challengeSubscription: {
challengeId: Challenge['id']
startTomorrow: boolean
}
}
export type CreateChallengeSubscriptionResponse =
| { success: false; message: string }
| { success: true; challengeSubscription: ChallengeSubscription }
/**
* Starts a user on a new challenge, creating a challenge subscription.
*
* @param userId The identifier assigned by Diet ID for the user.
* @param challengeId The identifier assigned by Diet ID for the challenge.
* @param startTomorrow (optional) Whether the user should start the challenge tomorrow instead of today (default: false).
*
* @returns The challenge subscription (join record between user and challenge).
*/
export const createChallengeSubscription = async (
challengeId: CreateChallengeSubscriptionBody['challengeSubscription']['challengeId'],
startTomorrow: CreateChallengeSubscriptionBody['challengeSubscription']['startTomorrow'] = false
) =>
request<CreateChallengeSubscriptionResponse>('POST', '/api/v2/challenge_subscriptions', {
challengeSubscription: { challengeId, startTomorrow }
})
createCheckin
import { request } from './util'
import { ChallengeSubscription, Checkin } from '../models'
export interface CreateCheckinBody {
checkin: {
challengeSubscriptionId: ChallengeSubscription['id']
successful: boolean
forYesterday: boolean
metadata?: object
}
}
export type CreateCheckinResponse = { success: false; message: string } | { success: true; checkin: Checkin }
/**
* Check in a user for a particular challenge.
*
* @param challengeSubscriptionId The id of the challenge subscription the user is checking in for.
* @param successful Whether the user indicated success or failure in the checkin.
* @param forYesterday (optional) Whether the checkin is for yesterday instead of today (default: false).
* @param metadata (optional) Arbitrary additional data you'd like to attach to the checkin.
*
* @returns The checkin record.
*/
export const createCheckin = (
challengeSubscriptionId: CreateCheckinBody['checkin']['challengeSubscriptionId'],
successful: CreateCheckinBody['checkin']['successful'],
forYesterday: CreateCheckinBody['checkin']['forYesterday'] = false,
metadata?: CreateCheckinBody['checkin']['metadata']
) =>
request<CreateCheckinResponse>('POST', '/api/v2/checkins', {
checkin: {
challengeSubscriptionId,
successful,
forYesterday,
metadata
}
})
createSSO (/api/v3)
import { request } from './util'
import { CreateUserBody } from './createUser'
export interface CreateSSOBody {
user: {
/**
* The user's unique identifier within a partner system (assigned by you).
*/
partnerUserId: User['partnerUserId']
/**
* The user's email address.
*/
email?: string
/**
* The user's first name.
*/
firstName?: string
/**
* The user's last name.
*/
lastName?: string
/**
* The user's desired username (optional).
*/
desiredUsername?: string
/**
* The user's gender (optional).
*/
gender?: enum:Gender
/**
* The user's postal code (optional).
*/
postalCode?: string
/**
* The user's date of birth formatted as YYYY-MM-DD (optional).
- 1999-11-12
*/
DOB?: string
/**
* The user's age in years (optional).
*/
age?: integer
/**
* The user's height in inches (optional).
*/
height?: integer
/**
* The user's weight in lbs (optional).
*/
weight?: integer
/**
* The user's activity level (optional).
*/
activityLevel?: enum:ActivityLevel
/**
* The user's pre-defined health goals (optional).
*/
healthGoals?: array:HealthGoal
/**
* The user's initial patient alert (optional).
*/
patientAlert?: string
/**
* The user's initial patient alert (optional).
*/
parentalConsentGiven?: bool
/**
* Any addditional user params (optional).
*/
additionalParams?: string - JSON blob
}
export type CreateSSOResponse = { success: false; message: string } | { success: true; url: string }
/**
* Returns a url the frontend can use to embed a Diet ID assessment in an iframe, webview, etc.
* The user is automatically signed in and prompted to complete the assessment.
*
* @param user The fields for finding or creating a Diet ID user (id required; email, firstName, and lastName optional)
* @returns A url that signs in a user and prompts them to complete an assessment.
*
*/
export const createSSO = (user: CreateSSOBody['user']) => request<CreateSSOResponse>('POST', '/api/v3/sso', { user })
createSSO (/api/v1) - Legacy
import { request } from './util'
import { CreateUserBody } from './createUser'
export type CreateSSOBody = CreateUserBody
export type CreateSSOResponse = { success: false; message: string } | { success: true; url: string }
/**
* Returns a url the frontend can use to embed a Diet ID assessment in an iframe, webview, etc.
* The user is automatically signed in and prompted to complete the assessment.
*
* @param user The fields for finding or creating a Diet ID user (id required; email, firstName, and lastName optional)
* @returns A url that signs in a user and prompts them to complete an assessment.
*
*/
export const createSSO = (user: CreateSSOBody['user']) => request<CreateSSOResponse>('POST', '/api/v1/sso', { user })
createUser
import { request } from './util'
import { User } from '../models'
export interface CreateUserBody {
user: {
/**
* The user's unique identifier within a partner system (assigned by you).
*/
partnerUserId: User['partnerUserId']
/**
* The user's email address.
*/
email?: string
/**
* The user's first name.
*/
firstName?: string
/**
* The user's last name.
*/
lastName?: string
}
}
export type CreateUserResponse = { success: false; message: string } | { success: true; user: User }
/**
* Creates a corresponding Diet ID user for a local user.
*
* @param user The required fields for creating a Diet ID user (id, email, first name, last name)
*
* @returns The newly created Diet ID user.
*/
export const createUser = async (user: CreateUserBody['user']) =>
request<CreateUserResponse>('POST', '/api/v2/users', { user })
getCurrentUser
import { request } from './util'
import { User } from '../models'
export type GetCurrentUserResponse = { success: false; message: string } | { success: true; user: User | null }
/**
* Returns the current Diet ID user.
*
* @returns The Diet ID user record.
*/
export const getCurrentUser = async () => request<GetCurrentUserResponse>('GET', '/api/v2/user')
getWorkflow
import { request } from './util'
import { Workflow } from '../models'
export interface GetWorkflowParams {
workflowId: Workflow['id']
}
export type GetWorkflowResponse =
| {
success: false
message: string
}
| { success: true; workflow: Workflow }
/**
* Returns the data collected and results from an assessment (workflow).
*
* @param workflowId The identifier assigned by Diet ID for the workflow.
*
* @returns The workflow with the id.
*/
export const getWorkflow = (workflowId: GetWorkflowParams['workflowId']) =>
request<GetWorkflowResponse>('GET', `/api/v2/workflows/${workflowId}`)
util
let gateway = 'http://localhost:3500'
/**
* Sets the url to the API Gateway all requests should route through.
*
* @param url The url to your API Gateway. e.g., https://yourapiserver.com/pathtodietidgateway
*/
export const setGateway = (url: string) => {
gateway = url
}
/**
* Possible HTTP verbs within Diet ID's API.
*/
type RequestMethod = 'GET' | 'POST'
/**
* Utility method for sending API requests to Diet ID.
*
* @param method The HTTP verb for the request ("GET", "POST", etc.)
* @param endpoint The url slug for a specific portion of the API
* @param body Optional data to be sent with POST and PUT requests
*
* @returns The JSON response from Diet ID for the API request.
*/
export const request = async <T>(method: RequestMethod, endpoint: string, body?: object): Promise<T> => {
if (!gateway) {
throw new Error("Please configure your API Gateway before making an API request (with setGateway('...'))")
}
const url = `${gateway}${endpoint}`
const headers = { Accepts: 'application/json', 'Content-Type': 'application/json' }
const data = JSON.stringify(body)
return (await fetch(url, { headers, method, body: data })).json()
}
index
export * from './createChallengeSubscription'
export * from './createCheckin'
export * from './createSSO'
export * from './createUser'
export * from './getCurrentUser'
export * from './getWorkflow'
export * from './util'