Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
81a830b
docs: update to address ticket #260246128
AJ-Koenig Apr 14, 2026
ae5722f
docs: update to address ticket #259933864
AJ-Koenig Apr 14, 2026
4a8c6dc
docs: update to address ticket #259927237
AJ-Koenig Apr 14, 2026
746007a
docs: update to address ticket #259783731
AJ-Koenig Apr 14, 2026
c22bbf0
docs: update to address ticket #259776328
AJ-Koenig Apr 14, 2026
4c5f708
docs: update to address ticket #259765954
AJ-Koenig Apr 14, 2026
c61e493
docs: update to address ticket #259719651
AJ-Koenig Apr 14, 2026
fa2ceca
docs: update to address ticket #259667631
AJ-Koenig Apr 14, 2026
4d649e8
docs: update to address ticket #259635369
AJ-Koenig Apr 14, 2026
0275fc0
docs: update to address ticket #259549656
AJ-Koenig Apr 14, 2026
6e329ed
docs: update to address ticket #259527533
AJ-Koenig Apr 14, 2026
03ba6bf
docs: update to address ticket #259333757
AJ-Koenig Apr 14, 2026
b61bd72
docs: update to address ticket #259225244
AJ-Koenig Apr 14, 2026
41e92a2
docs: update to address ticket #259048407
AJ-Koenig Apr 14, 2026
099edd0
docs: update to address ticket #258666553
AJ-Koenig Apr 14, 2026
f2fd0b9
docs: update to address ticket #258623832
AJ-Koenig Apr 14, 2026
12c4275
docs: update to address ticket #258581407
AJ-Koenig Apr 14, 2026
6c87127
docs: update to address ticket #258436971
AJ-Koenig Apr 14, 2026
bb18c39
docs: update to address ticket #258395793
AJ-Koenig Apr 14, 2026
f7fa79b
docs: update to address ticket #258383635
AJ-Koenig Apr 14, 2026
53d66b1
docs: update to address ticket #258345677
AJ-Koenig Apr 14, 2026
afd1e78
docs: update to address ticket #258316618
AJ-Koenig Apr 14, 2026
82cfec8
docs: update to address ticket #258275171
AJ-Koenig Apr 14, 2026
c37ea89
docs: update to address ticket #257826751
AJ-Koenig Apr 14, 2026
ac96aed
docs: update to address ticket #257794102
AJ-Koenig Apr 14, 2026
0ae5655
docs: update to address ticket #257745938
AJ-Koenig Apr 14, 2026
39581ee
docs: update to address ticket #257709854
AJ-Koenig Apr 14, 2026
187a126
docs: update to address ticket #257579917
AJ-Koenig Apr 14, 2026
24413cb
docs: update to address ticket #257567645
AJ-Koenig Apr 14, 2026
9780cd7
docs: update to address ticket #257543808
AJ-Koenig Apr 14, 2026
74383bf
docs: update to address ticket #257311983
AJ-Koenig Apr 14, 2026
ea342db
docs: update to address ticket #257183895
AJ-Koenig Apr 14, 2026
154ce0c
docs: update to address ticket #257182500
AJ-Koenig Apr 14, 2026
38f88ab
docs: update to address ticket #257064613
AJ-Koenig Apr 14, 2026
06f6175
docs: update to address ticket #256763165
AJ-Koenig Apr 14, 2026
0fe84d0
docs: update to address ticket #256344641
AJ-Koenig Apr 14, 2026
fb42306
docs: update to address ticket #256320419
AJ-Koenig Apr 14, 2026
97e8d75
docs: update to address ticket #256114436
AJ-Koenig Apr 14, 2026
c92d564
docs: update to address ticket #256103387
AJ-Koenig Apr 14, 2026
5cefa1c
docs: update to address ticket #256063009
AJ-Koenig Apr 14, 2026
5bdd923
docs: update to address ticket #256062170
AJ-Koenig Apr 14, 2026
e9978dc
docs: update to address ticket #256015115
AJ-Koenig Apr 14, 2026
03d63df
docs: update to address ticket #255874717
AJ-Koenig Apr 14, 2026
e7d58c4
docs: update to address ticket #255684769
AJ-Koenig Apr 14, 2026
97a969d
docs: update to address ticket #255654496
AJ-Koenig Apr 14, 2026
9dff25b
docs: update to address ticket #255589527
AJ-Koenig Apr 14, 2026
98a2d1e
docs: update to address ticket #255576760
AJ-Koenig Apr 14, 2026
4b462ca
docs: update to address ticket #255500632
AJ-Koenig Apr 14, 2026
129f1bc
docs: update to address ticket #255415262
AJ-Koenig Apr 14, 2026
7dbf478
docs: update to address ticket #255270087
AJ-Koenig Apr 14, 2026
6dac65e
docs: update to address ticket #255141826
AJ-Koenig Apr 14, 2026
d1b849c
docs: update to address ticket #255137092
AJ-Koenig Apr 14, 2026
6dadeb0
docs: update to address ticket #254905003
AJ-Koenig Apr 14, 2026
2092615
docs: update to address ticket #254899480
AJ-Koenig Apr 14, 2026
3dc1aed
docs: update to address ticket #254814409
AJ-Koenig Apr 14, 2026
000f667
docs: update to address ticket #254234289
AJ-Koenig Apr 14, 2026
802cd29
docs: update to address ticket #254145925
AJ-Koenig Apr 14, 2026
c96fa86
docs: update to address ticket #254139549
AJ-Koenig Apr 14, 2026
dd0064b
docs: update to address ticket #254076057
AJ-Koenig Apr 14, 2026
b924312
docs: update to address ticket #253998043
AJ-Koenig Apr 14, 2026
55e1cf7
docs: update to address ticket #253966761
AJ-Koenig Apr 14, 2026
66a7da3
docs: update to address ticket #253964305
AJ-Koenig Apr 14, 2026
640ab25
docs: update to address ticket #253928559
AJ-Koenig Apr 14, 2026
ab14f82
docs: update to address ticket #253724962
AJ-Koenig Apr 14, 2026
c8ddd05
docs: update to address ticket #253578631
AJ-Koenig Apr 14, 2026
552a4a8
docs: update to address ticket #253538578
AJ-Koenig Apr 14, 2026
7ccb106
docs: update to address ticket #253459514
AJ-Koenig Apr 14, 2026
b6aaab3
docs: update to address ticket #253437395
AJ-Koenig Apr 14, 2026
9a336e9
docs: update to address ticket #253385491
AJ-Koenig Apr 14, 2026
dc1f25d
docs: update to address ticket #253325287
AJ-Koenig Apr 14, 2026
3aa0636
docs: update to address ticket #253274853
AJ-Koenig Apr 14, 2026
9fe24e6
docs: update to address ticket #253252036
AJ-Koenig Apr 14, 2026
672cfcb
docs: update to address ticket #253217857
AJ-Koenig Apr 14, 2026
20e3f6b
docs: update to address ticket #253079154
AJ-Koenig Apr 14, 2026
12ac4dc
docs: update to address ticket #252919767
AJ-Koenig Apr 14, 2026
b16a991
docs: update to address ticket #252880220
AJ-Koenig Apr 14, 2026
e3423e9
docs: update to address ticket #252866707
AJ-Koenig Apr 14, 2026
eca7811
docs: update to address ticket #252837673
AJ-Koenig Apr 14, 2026
c631f99
docs: update to address ticket #252716450
AJ-Koenig Apr 14, 2026
fa32c66
docs: update to address ticket #252690527
AJ-Koenig Apr 14, 2026
2d9bcbe
docs: update to address ticket #252683970
AJ-Koenig Apr 14, 2026
a7b6fda
docs: update to address ticket #252599316
AJ-Koenig Apr 14, 2026
d164b3c
docs: update to address ticket #252595069
AJ-Koenig Apr 14, 2026
2e18771
docs: update to address ticket #252564942
AJ-Koenig Apr 14, 2026
e91e3c3
chore: update gorgias backfill run log
AJ-Koenig Apr 14, 2026
92e972b
docs: update to address ticket #259819995
Apr 14, 2026
c407258
docs: update to address ticket #260506038
Apr 14, 2026
9c6aa6d
docs: update to address ticket #259679346
Apr 14, 2026
a255d31
docs: update to address ticket #258325041
Apr 14, 2026
4444bc6
docs: update to address ticket #258750459
Apr 14, 2026
be3025c
docs: update to address ticket #259698311
Apr 14, 2026
43eb512
docs: update to address ticket #259437392
Apr 14, 2026
a602c23
docs: update to address ticket #259253168
Apr 14, 2026
6a8436c
docs: update to address ticket #258344690
Apr 14, 2026
c2f5f44
docs: update to address ticket #258803774
Apr 14, 2026
043ad7f
docs: update to address ticket #257905976
Apr 14, 2026
6b20224
docs: update to address ticket #256882767
Apr 14, 2026
a64315c
docs: update to address ticket #260873743
Apr 15, 2026
3ecf213
docs: update to address ticket #260694995
Apr 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .cursor/debug-f15264.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{"sessionId": "f15264", "hypothesisId": "H1", "location": "pre_build_script.py", "message": "udp_trick_primary_ip", "data": {"primary_ip": "100.64.100.6", "is_cgnat_100_64": true}, "timestamp": 1775579962176, "runId": "build"}
{"sessionId": "f15264", "hypothesisId": "H1", "location": "pre_build_script.py", "message": "cgnat_fallback_attempt", "data": {"primary_ip": "100.64.100.6", "fallback_ip": "192.168.1.173"}, "timestamp": 1775579962184, "runId": "build"}
{"sessionId": "f15264", "hypothesisId": "H1", "location": "pre_build_script.py", "message": "udp_trick_primary_ip", "data": {"primary_ip": "100.64.100.6", "is_cgnat_100_64": true}, "timestamp": 1775579962184, "runId": "build"}
{"sessionId": "f15264", "hypothesisId": "H1", "location": "pre_build_script.py", "message": "cgnat_fallback_attempt", "data": {"primary_ip": "100.64.100.6", "fallback_ip": "192.168.1.173"}, "timestamp": 1775579962190, "runId": "build"}
{"sessionId": "f15264", "hypothesisId": "H1", "location": "pre_build_script.py", "message": "localhost_replacements", "data": {"local_ip": "192.168.1.173", "pairs": [["-D RAD_SERVER=\"\\\"http://localhost:3000\\\"\"", "-D RAD_SERVER=\"\\\"http://192.168.1.173:3000\\\"\""], ["-D MQTT_SERVER=\"\\\"\"mqtt://localhost\"\\\"\"", "-D MQTT_SERVER=\"\\\"\"mqtt://192.168.1.173\"\\\"\""], ["-D RAD_SERVER_SHORT=\"\\\"http://localhost:3000\\\"\"", "-D RAD_SERVER_SHORT=\"\\\"http://192.168.1.173:3000\\\"\""]]}, "timestamp": 1775579962190, "runId": "build"}
{"sessionId": "f15264", "hypothesisId": "H1", "location": "pre_build_script.py", "message": "udp_trick_primary_ip", "data": {"primary_ip": "100.64.100.6", "is_cgnat_100_64": true}, "timestamp": 1775579966754, "runId": "build"}
{"sessionId": "f15264", "hypothesisId": "H1", "location": "pre_build_script.py", "message": "cgnat_fallback_attempt", "data": {"primary_ip": "100.64.100.6", "fallback_ip": "192.168.1.173"}, "timestamp": 1775579966766, "runId": "build"}
{"sessionId": "f15264", "hypothesisId": "H1", "location": "pre_build_script.py", "message": "udp_trick_primary_ip", "data": {"primary_ip": "100.64.100.6", "is_cgnat_100_64": true}, "timestamp": 1775579966766, "runId": "build"}
{"sessionId": "f15264", "hypothesisId": "H1", "location": "pre_build_script.py", "message": "cgnat_fallback_attempt", "data": {"primary_ip": "100.64.100.6", "fallback_ip": "192.168.1.173"}, "timestamp": 1775579966786, "runId": "build"}
{"sessionId": "f15264", "hypothesisId": "H1", "location": "pre_build_script.py", "message": "localhost_replacements", "data": {"local_ip": "192.168.1.173", "pairs": [["-D RAD_SERVER=\"\\\"http://localhost:3000\\\"\"", "-D RAD_SERVER=\"\\\"http://192.168.1.173:3000\\\"\""], ["-D MQTT_SERVER=\"\\\"\"mqtt://localhost\"\\\"\"", "-D MQTT_SERVER=\"\\\"\"mqtt://192.168.1.173\"\\\"\""], ["-D RAD_SERVER_SHORT=\"\\\"http://localhost:3000\\\"\"", "-D RAD_SERVER_SHORT=\"\\\"http://192.168.1.173:3000\\\"\""]]}, "timestamp": 1775579966786, "runId": "build"}
1 change: 1 addition & 0 deletions .cursor/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
139 changes: 117 additions & 22 deletions Documentation/_scripts/commands/gorgias-tickets.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,19 @@ export default class GorgiasTickets extends Command {
description: 'Analyze tickets for documentation gaps and update docs if needed',
default: false,
}),
since: Flags.string({
description:
'Only include tickets closed on or after this instant (ISO 8601, e.g. 2026-02-25T01:15:00+07:00)',
env: 'GORGIAS_SINCE',
}),
'ticket-id': Flags.integer({
char: 't',
description: 'Analyze a single ticket by ID (skips listing/pagination)',
}),
'list-only': Flags.boolean({
description: 'Print matching ticket IDs and exit (no analysis)',
default: false,
}),
};

/**
Expand Down Expand Up @@ -266,7 +279,7 @@ export default class GorgiasTickets extends Command {
/**
* Fetch all ticket IDs with pagination
*/
async fetchAllTicketIds(limit, maxPages, viewId, allStatuses = false) {
async fetchAllTicketIds(limit, maxPages, viewId, allStatuses = false, sinceDate = null) {
const ticketIds = [];
let cursor = null;
let pageCount = 0;
Expand All @@ -286,11 +299,18 @@ export default class GorgiasTickets extends Command {
: tickets.filter((t) => t.status === 'closed');

for (const ticket of filteredTickets) {
const closedAt = ticket.closed_datetime ? new Date(ticket.closed_datetime) : null;
if (sinceDate) {
if (!closedAt || Number.isNaN(closedAt.getTime()) || closedAt < sinceDate) {
continue;
}
}
ticketIds.push({
id: ticket.id,
subject: ticket.subject,
channel: ticket.channel,
created_datetime: ticket.created_datetime,
closed_datetime: ticket.closed_datetime || null,
customer_name: ticket.customer?.name || ticket.customer?.email || null,
});
}
Expand Down Expand Up @@ -410,7 +430,7 @@ When rewriting apply these rules:

try {
const result = execSync(
`agent --print --model opus-4.5-thinking --force --workspace "${docsRoot}" ${escapedPrompt}`,
`agent --print --model composer-2-fast --force --workspace "${docsRoot}" ${escapedPrompt}`,
{
cwd: docsRoot,
encoding: 'utf-8',
Expand All @@ -429,6 +449,13 @@ When rewriting apply these rules:

return false;
} catch (error) {
const stderr = error.stderr ? error.stderr.toString() : '';
const combined = `${stderr} ${error.message || ''}`;
if (combined.includes('Authentication required') || combined.includes('CURSOR_API_KEY')) {
throw new Error(
'Agent authentication failed. Set CURSOR_API_KEY in Documentation/.env.local or run `agent login`.'
);
}
this.log(` ${c.red}⚠ AI analysis error: ${error.message}${c.reset}`);
return false;
}
Expand Down Expand Up @@ -490,48 +517,116 @@ When rewriting apply these rules:
const maxPages = flags['max-pages'];
const viewId = flags['view-id'];
const allStatuses = flags['all-statuses'];
const singleTicketId = flags['ticket-id'];
const listOnly = flags['list-only'];

let sinceDate = null;
if (flags.since) {
sinceDate = new Date(flags.since);
if (Number.isNaN(sinceDate.getTime())) {
this.error(`Invalid --since value: ${flags.since}`);
}
}

const docsRoot = join(__dirname, '..', '..');

try {
if (singleTicketId) {
const details = await this.fetchTicketDetails(singleTicketId);
const messages = this.extractMessagesForAI(details);
const { tags, contactReason, product } = this.extractTicketMetadata(details);
const conversations = [
{
ticket_id: details.id,
subject: details.subject,
channel: details.channel,
contact_reason: contactReason,
product,
tags,
messages,
},
];

if (listOnly) {
this.log(String(singleTicketId));
return;
}

if (!flags.analyze) {
this.log(
`${c.yellow}⚠ Single-ticket mode: pass --analyze to run the agent, or --list-only.${c.reset}`
);
return;
}

const results = await this.analyzeTickets(conversations, docsRoot);
this.printSummary(results);
return;
}

// Step 1: Get all ticket IDs
const ticketInfos = await this.fetchAllTicketIds(limit, maxPages, viewId, allStatuses);
const ticketInfos = await this.fetchAllTicketIds(
limit,
maxPages,
viewId,
allStatuses,
sinceDate
);

if (ticketInfos.length === 0) {
this.log(`${c.yellow}⚠ No tickets found.${c.reset}`);
return;
}

if (listOnly) {
this.log(`${c.cyan}Matching closed tickets: ${ticketInfos.length}${c.reset}`);
for (const t of ticketInfos) {
this.log(String(t.id));
}
return;
}

// Step 2: Fetch full details for each ticket
const conversations = await this.fetchAllConversations(ticketInfos);


// Step 3: Analyze tickets and update docs
if (!flags.analyze) {
this.log(
`${c.yellow}⚠ Fetched ${conversations.length} conversations; pass --analyze to run the agent.${c.reset}`
);
return;
}

const results = await this.analyzeTickets(conversations, docsRoot);

// Summary
this.log(`\n${c.bold}${c.white}${'═'.repeat(80)}${c.reset}`);
this.log(`${c.bold}${c.white}📊 SUMMARY${c.reset}`);
this.log(`${c.bold}${c.white}${'═'.repeat(80)}${c.reset}`);
this.printSummary(results);

const docsCreatedCount = results.filter((r) => r.docs_created).length;
const noDocsCount = results.filter((r) => !r.docs_created).length;
} catch (error) {
this.error(error.message);
}
}

this.log(`\n ${c.cyan}Total tickets analyzed:${c.reset} ${c.bold}${results.length}${c.reset}`);
this.log(` ${c.green}Documentation created/updated:${c.reset} ${c.bold}${c.green}${docsCreatedCount}${c.reset}`);
this.log(` ${c.yellow}No documentation needed:${c.reset} ${c.bold}${noDocsCount}${c.reset}`);
printSummary(results) {
this.log(`\n${c.bold}${c.white}${'═'.repeat(80)}${c.reset}`);
this.log(`${c.bold}${c.white}📊 SUMMARY${c.reset}`);
this.log(`${c.bold}${c.white}${'═'.repeat(80)}${c.reset}`);

if (docsCreatedCount > 0) {
this.log(`\n${c.green}${c.bold}📝 Tickets with new docs:${c.reset}`);
for (const r of results.filter((r) => r.docs_created)) {
this.log(` ${c.green}•${c.reset} ${c.yellow}#${r.ticket_id}${c.reset}: ${r.subject}`);
}
}
const docsCreatedCount = results.filter((r) => r.docs_created).length;
const noDocsCount = results.filter((r) => !r.docs_created).length;

this.log('');
this.log(`\n ${c.cyan}Total tickets analyzed:${c.reset} ${c.bold}${results.length}${c.reset}`);
this.log(
` ${c.green}Documentation created/updated:${c.reset} ${c.bold}${c.green}${docsCreatedCount}${c.reset}`
);
this.log(` ${c.yellow}No documentation needed:${c.reset} ${c.bold}${noDocsCount}${c.reset}`);

} catch (error) {
this.error(error.message);
if (docsCreatedCount > 0) {
this.log(`\n${c.green}${c.bold}📝 Tickets with new docs:${c.reset}`);
for (const r of results.filter((r) => r.docs_created)) {
this.log(` ${c.green}•${c.reset} ${c.yellow}#${r.ticket_id}${c.reset}: ${r.subject}`);
}
}

this.log('');
}
}
30 changes: 30 additions & 0 deletions Documentation/dashboard/api/dtt-session-metrics.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
title: "DTT session metrics"
description: "How to interpret reps, distance, and deepthroat fields on DTT session and segment payloads from the API"
---

Responses from `GET /api/v1/dtt/sessions`, `GET /api/v1/dtt/sessions/latest`, and `GET /api/v1/dtt/sessions/{id}` include per-session and per-segment metrics. The field names below match the JSON returned by the API.

## `repsMeasured`

One **rep** is a full in-and-out cycle (insert and withdraw). The firmware counts motion in half-cycles (“strokes”); stored **reps** are half that count, so the value represents complete cycles as you would count them in training.

## `distanceTravelled`

For current firmware and the normal upload path, this is the **total distance moved along the depth axis** during the session or segment, in **meters**. Insertion and withdrawal both contribute. The value is built from small position steps on the device, not a simple `reps × stroke length` formula.

<Info>
Older sessions that used a **legacy upload path** may compute distance differently (for example, estimated from rep count and configured depth). When comparing very old data to recent sessions, treat distance as approximate unless you know the upload path.
</Info>

## `longestDeepthroat`

For the current API, this value is the **total time spent in the target depth window** for that segment, in **milliseconds**. It is **cumulative** time in-zone across the segment, not the length of a single uninterrupted hold—the name can read like “longest streak,” but the stored number is total in-zone duration. Dashboards and UIs often display it in seconds by dividing by 1000.

<Warning>
Do not interpret `longestDeepthroat` as “longest single continuous hold” unless you have verified that against your integration; the API stores cumulative in-zone time.
</Warning>

## Related endpoints

See the **DTT Sessions** operations in the API reference for request parameters and full response shapes.
49 changes: 49 additions & 0 deletions Documentation/dashboard/faqs/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,18 @@ Get quick answers to the most common questions about using the dashboard.

Our support team will investigate and restore access to your account.

</Accordion>
<Accordion title="I see a technical error when signing in, or only some dashboard pages load">
Errors that mention **Server Components** or other technical wording usually come from a temporary problem in the web app or from something stuck in your browser session—not from your password being wrong.

Try these steps in order:

1. **Hard refresh** the page (reload while bypassing cache) — on desktop, use your browser’s shortcut for a hard refresh, or close the tab and open the dashboard again.
2. **Try another browser** or a **private/incognito** window to rule out extensions or cached data.
3. If password sign-in fails but you still need access, use **Email link** on the [sign-in page](https://dashboard.researchanddesire.com/app) and sign in with the link or code from your email — see <a href="/dashboard/faqs/forgot-password">Reset your password</a>.

If the problem continues, email [support@researchanddesire.com](mailto:support@researchanddesire.com) with your account email, which browser you use, and a screenshot of the full error if you can.

</Accordion>
<Accordion title="How do I change my email address?">
You can change your email without leaving the page:
Expand Down Expand Up @@ -133,6 +145,19 @@ Get quick answers to the most common questions about using the dashboard.
</Note>

</Accordion>
<Accordion title="How do I remove a partner from my account?">
If you no longer want someone to have access to your devices:

1. Sign in to the dashboard
2. Go to **Settings → Partners**
3. Open **Manage** on the connection and choose **Remove partner**

Removing a partner immediately revokes their access to your account. For the full walkthrough, see{" "}
<a href="/dashboard/quick-start/connections/managing-partners">Managing Partner Connections</a>.

If you use a Chastity Lockbox and you cannot resolve things from the dashboard—for example an active lock with no cooperative keyholder, you are locked out after **Give Up Control**, or a session is stuck after removing a partner—contact [support@researchanddesire.com](mailto:support@researchanddesire.com) with your account email. Our team can help end the session and restore your access when needed. For Lockbox-specific scenarios, see{" "}
<a href="/lkbx/faqs">Chastity Lockbox FAQs</a>.
</Accordion>

</AccordionGroup>

Expand Down Expand Up @@ -224,6 +249,18 @@ Get quick answers to the most common questions about using the dashboard.
</Note>
Mobile app development is on our roadmap for future releases.

</Accordion>
<Accordion title="On iPhone, the Save button is hard to tap or the browser back control gets in the way">
The dashboard runs in your web browser (for example Safari or Chrome)—not a separate App Store app. On iOS, **toolbars at the bottom of the screen** (tabs, navigation, address bar) can sit on top of buttons that are fixed low on the page, including **Save** in some dialogs (such as hygiene break settings under **Chastity Lockbox → Home → Modify Settings**).

Try one of these:

1. **Scroll** so the **Save** control sits **above** the browser toolbar, then tap it carefully.
2. **Rotate to landscape** or use a **larger window** so more of the page is visible.
3. Complete the change on a **tablet or computer** in the same browser session if taps keep mis-registering.

If a tap sends you back to **Home** without saving, reopen **Modify Settings**, adjust again, and use the steps above. For persistent issues, email [support@researchanddesire.com](mailto:support@researchanddesire.com).

</Accordion>
<Accordion title="Why does my scheduled template show the wrong day?">
The dashboard uses GMT (Greenwich Mean Time) for all scheduling features. If you start a session in the evening in your local timezone, the dashboard may already consider it the next day in GMT.
Expand All @@ -235,6 +272,18 @@ Get quick answers to the most common questions about using the dashboard.
</Note>

</Accordion>
<Accordion title="My Deepthroat Trainer activity isn't showing on the dashboard">
Session data uploads when your Trainer completes a session while connected to Wi‑Fi (not offline-only playback). If nothing new appears:

1. Wait a minute, then **refresh** the dashboard and open **Deepthroat Trainer** to review the activity log.
2. Confirm the Trainer was online during the session and is [paired to your account](/dtt/quick-start/pairing/pairing-guide).

Full troubleshooting—including points, leaderboards, and what to do when a session never appears—is in{" "}
<a href="/dtt/quick-start/checking-results">Checking your results</a>.

If you were connected throughout training and sessions still do not show after refreshing, email [support@researchanddesire.com](mailto:support@researchanddesire.com) with your account email and approximately when you trained.

</Accordion>
</AccordionGroup>

## Support
Expand Down
Loading
Loading