vybekiit 0.7.33 → 0.7.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/dist/bin.js +818 -92
  2. package/dist/global-skills/eas-app-stores/SKILL.md +2 -2
  3. package/dist/global-skills/eas-hosting/SKILL.md +2 -2
  4. package/dist/global-skills/eas-observe/SKILL.md +7 -15
  5. package/dist/global-skills/eas-observe/references/queries.md +51 -10
  6. package/dist/global-skills/eas-observe/references/setup.md +60 -7
  7. package/dist/global-skills/eas-simulator/SKILL.md +2 -2
  8. package/dist/global-skills/eas-simulator/references/run-your-app.md +2 -0
  9. package/dist/global-skills/eas-update/SKILL.md +2 -2
  10. package/dist/global-skills/eas-update-insights/SKILL.md +2 -2
  11. package/dist/global-skills/eas-workflows/SKILL.md +2 -2
  12. package/dist/global-skills/expo-animation/SKILL.md +2 -2
  13. package/dist/global-skills/expo-app-clip/SKILL.md +2 -2
  14. package/dist/global-skills/expo-brownfield/SKILL.md +2 -2
  15. package/dist/global-skills/expo-data-fetching/SKILL.md +2 -2
  16. package/dist/global-skills/expo-design-system/SKILL.md +2 -2
  17. package/dist/global-skills/expo-dev-client/SKILL.md +4 -2
  18. package/dist/global-skills/expo-dom/SKILL.md +2 -2
  19. package/dist/global-skills/expo-examples/SKILL.md +2 -2
  20. package/dist/global-skills/expo-migrate-module/SKILL.md +2 -2
  21. package/dist/global-skills/expo-module/SKILL.md +2 -2
  22. package/dist/global-skills/expo-native-ui/SKILL.md +2 -2
  23. package/dist/global-skills/expo-overview/SKILL.md +9 -4
  24. package/dist/global-skills/expo-overview/references/launch-without-dev-menu.md +24 -0
  25. package/dist/global-skills/expo-project-structure/SKILL.md +2 -2
  26. package/dist/global-skills/expo-router/SKILL.md +2 -2
  27. package/dist/global-skills/expo-ui/SKILL.md +2 -2
  28. package/dist/global-skills/expo-upgrade/SKILL.md +2 -2
  29. package/dist/global-skills/expo-web-to-native/SKILL.md +2 -2
  30. package/dist/global-skills/expo-web-to-native/references/verify-on-device.md +1 -1
  31. package/dist/global-skills/firebase-app-hosting-basics/SKILL.md +16 -3
  32. package/dist/global-skills/firebase-app-hosting-basics/references/cli_commands.md +10 -12
  33. package/dist/global-skills/firebase-app-hosting-basics/references/configuration.md +2 -1
  34. package/dist/global-skills/firebase-basics/SKILL.md +6 -4
  35. package/dist/global-skills/firebase-basics/references/web_setup.md +3 -3
  36. package/dist/global-skills/instrument-feature-flags/references/adding-feature-flag-code.md +81 -333
  37. package/dist/global-skills/instrument-feature-flags/references/android.md +19 -13
  38. package/dist/global-skills/instrument-feature-flags/references/api.md +17 -13
  39. package/dist/global-skills/instrument-feature-flags/references/best-practices.md +35 -35
  40. package/dist/global-skills/instrument-feature-flags/references/django.md +120 -72
  41. package/dist/global-skills/instrument-feature-flags/references/dotnet.md +48 -102
  42. package/dist/global-skills/instrument-feature-flags/references/elixir.md +4 -10
  43. package/dist/global-skills/instrument-feature-flags/references/flask.md +55 -39
  44. package/dist/global-skills/instrument-feature-flags/references/flutter.md +19 -13
  45. package/dist/global-skills/instrument-feature-flags/references/go.md +21 -13
  46. package/dist/global-skills/instrument-feature-flags/references/ios.md +19 -13
  47. package/dist/global-skills/instrument-feature-flags/references/java.md +4 -12
  48. package/dist/global-skills/instrument-feature-flags/references/laravel.md +18 -16
  49. package/dist/global-skills/instrument-feature-flags/references/next-js.md +44 -54
  50. package/dist/global-skills/instrument-feature-flags/references/nodejs.md +21 -13
  51. package/dist/global-skills/instrument-feature-flags/references/php.md +21 -13
  52. package/dist/global-skills/instrument-feature-flags/references/python.md +22 -14
  53. package/dist/global-skills/instrument-feature-flags/references/react-native.md +18 -13
  54. package/dist/global-skills/instrument-feature-flags/references/react.md +21 -14
  55. package/dist/global-skills/instrument-feature-flags/references/ruby-on-rails.md +31 -68
  56. package/dist/global-skills/instrument-feature-flags/references/ruby.md +21 -13
  57. package/dist/global-skills/instrument-feature-flags/references/rust.md +10 -34
  58. package/dist/global-skills/instrument-feature-flags/references/usage.md +94 -114
  59. package/dist/global-skills/instrument-feature-flags/references/web.md +21 -13
  60. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-android.md +7 -7
  61. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-expo.md +2 -2
  62. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-fastapi.md +1 -1
  63. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-flask.md +1 -1
  64. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-laravel.md +2 -2
  65. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-php.md +1 -1
  66. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-python.md +1 -1
  67. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-ruby.md +1 -1
  68. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-swift.md +4 -4
  69. package/dist/global-skills/instrument-product-analytics/references/android.md +64 -109
  70. package/dist/global-skills/instrument-product-analytics/references/angular.md +32 -28
  71. package/dist/global-skills/instrument-product-analytics/references/astro.md +29 -34
  72. package/dist/global-skills/instrument-product-analytics/references/configuration.md +27 -42
  73. package/dist/global-skills/instrument-product-analytics/references/django.md +120 -72
  74. package/dist/global-skills/instrument-product-analytics/references/dotnet.md +48 -102
  75. package/dist/global-skills/instrument-product-analytics/references/elixir.md +25 -50
  76. package/dist/global-skills/instrument-product-analytics/references/flask.md +55 -39
  77. package/dist/global-skills/instrument-product-analytics/references/flutter.md +94 -96
  78. package/dist/global-skills/instrument-product-analytics/references/go.md +39 -77
  79. package/dist/global-skills/instrument-product-analytics/references/identify-users.md +24 -26
  80. package/dist/global-skills/instrument-product-analytics/references/ios.md +22 -22
  81. package/dist/global-skills/instrument-product-analytics/references/laravel.md +18 -16
  82. package/dist/global-skills/instrument-product-analytics/references/next-js.md +44 -54
  83. package/dist/global-skills/instrument-product-analytics/references/nuxt-js-3-6.md +32 -34
  84. package/dist/global-skills/instrument-product-analytics/references/nuxt-js.md +36 -36
  85. package/dist/global-skills/instrument-product-analytics/references/php.md +42 -87
  86. package/dist/global-skills/instrument-product-analytics/references/posthog-python.md +15 -12
  87. package/dist/global-skills/instrument-product-analytics/references/python.md +106 -154
  88. package/dist/global-skills/instrument-product-analytics/references/react-native.md +97 -176
  89. package/dist/global-skills/instrument-product-analytics/references/react-router-v6.md +29 -40
  90. package/dist/global-skills/instrument-product-analytics/references/react-router-v7-data-mode.md +29 -38
  91. package/dist/global-skills/instrument-product-analytics/references/react-router-v7-declarative-mode.md +29 -40
  92. package/dist/global-skills/instrument-product-analytics/references/react-router-v7-framework-mode.md +32 -37
  93. package/dist/global-skills/instrument-product-analytics/references/ruby-on-rails.md +31 -68
  94. package/dist/global-skills/instrument-product-analytics/references/ruby.md +40 -96
  95. package/dist/global-skills/instrument-product-analytics/references/svelte.md +34 -34
  96. package/dist/global-skills/instrument-product-analytics/references/tanstack-start.md +19 -19
  97. package/dist/global-skills/instrument-product-analytics/references/usage.md +94 -114
  98. package/dist/global-skills/instrument-product-analytics/references/vue-js.md +33 -45
  99. package/dist/global-skills/manifest.json +2 -1
  100. package/dist/global-skills/mongodb-search-and-ai/SKILL.md +40 -22
  101. package/dist/global-skills/mongodb-search-and-ai/references/automated-embedding.md +8 -2
  102. package/dist/global-skills/mongodb-search-and-ai/references/quick-start.md +725 -0
  103. package/dist/global-skills/mongodb-search-and-ai/scripts/quickstart_complete.py +386 -0
  104. package/dist/global-skills/neon/SKILL.md +15 -10
  105. package/dist/global-skills/neon/references/claimable-neon.md +2 -0
  106. package/dist/global-skills/neon-functions/SKILL.md +12 -11
  107. package/dist/global-skills/neon-functions/references/sse.md +2 -2
  108. package/dist/global-skills/neon-object-storage/SKILL.md +5 -4
  109. package/dist/global-skills/neon-postgres/SKILL.md +4 -1
  110. package/dist/global-skills/neon-realtime/SKILL.md +207 -0
  111. package/dist/global-skills/neon-realtime/references/mutations.md +743 -0
  112. package/dist/global-skills/neon-realtime/references/queries.md +509 -0
  113. package/dist/global-skills/neon-realtime/references/reactivity.md +407 -0
  114. package/dist/global-skills/neon-realtime/references/ssr.md +695 -0
  115. package/dist/global-skills/neon-realtime/references/subscribing.md +485 -0
  116. package/dist/global-skills/resend/SKILL.md +4 -2
  117. package/dist/global-skills/resend/references/inboxes.md +377 -0
  118. package/dist/global-skills/resend/references/webhooks.md +9 -0
  119. package/dist/global-skills/stripe-best-practices/SKILL.md +1 -1
  120. package/dist/global-skills/stripe-best-practices/references/connect.md +1 -1
  121. package/dist/global-skills/stripe-best-practices/references/security.md +9 -9
  122. package/dist/global-skills/vybekiitReferences/backend/AGENTS.md +1 -1
  123. package/dist/global-skills/vybekiitReferences/backend/agent/goal-routes.json +303 -18
  124. package/dist/global-skills/vybekiitReferences/extension/AGENTS.md +1 -1
  125. package/dist/global-skills/vybekiitReferences/extension/agent/goal-routes.json +352 -18
  126. package/dist/global-skills/vybekiitReferences/mobile/AGENTS.md +1 -1
  127. package/dist/global-skills/vybekiitReferences/mobile/agent/goal-routes.json +413 -22
  128. package/dist/global-skills/vybekiitReferences/spa/AGENTS.md +1 -1
  129. package/dist/global-skills/vybekiitReferences/spa/agent/goal-routes.json +391 -21
  130. package/dist/global-skills/vybekiitReferences/web/AGENTS.md +14 -2
  131. package/dist/global-skills/vybekiitReferences/web/agent/goal-routes.json +490 -32
  132. package/package.json +8 -8
@@ -0,0 +1,377 @@
1
+ # Inboxes
2
+
3
+ An inbox sends and receives email at one address on your domain. Received and sent emails group into threads. Each thread is in one folder and can carry labels. Drafts hold unsent emails, and agent settings tell an AI agent how to handle the threads of the inbox.
4
+
5
+ Inboxes need Node.js `resend` 6.34.1 or later, or Python `resend` 2.50.0 or later.
6
+
7
+ If an agent acts on the content of received emails, also install the `agent-email-inbox` skill. It covers the security levels for untrusted input.
8
+
9
+ ## SDK Methods
10
+
11
+ | Operation | Node.js | Python |
12
+ |-----------|---------|--------|
13
+ | Create inbox | `resend.inboxes.create(params)` | `resend.Inboxes.create(params)` |
14
+ | List inboxes | `resend.inboxes.list(params?)` | `resend.Inboxes.list(params?)` |
15
+ | Get inbox | `resend.inboxes.get(idOrEmail)` | `resend.Inboxes.get(inbox_id)` |
16
+ | Update inbox | `resend.inboxes.update(id, params)` | `resend.Inboxes.update(params)`, `id` inside params |
17
+ | Delete inbox | `resend.inboxes.remove(id)` | `resend.Inboxes.remove(inbox_id)` |
18
+ | Get agent settings | `resend.inboxes.agent.get({ inboxId })` | `resend.Inboxes.Agent.get(inbox_id)` |
19
+ | Update agent settings | `resend.inboxes.agent.update({ inboxId, ...params })` | `resend.Inboxes.Agent.update(params)`, `inbox_id` inside params |
20
+ | List labels | `resend.inboxes.labels.list({ inboxId })` | `resend.Inboxes.Labels.list(inbox_id)` |
21
+ | Create label | `resend.inboxes.labels.create({ inboxId, ...params })` | `resend.Inboxes.Labels.create(params)`, `inbox_id` inside params |
22
+ | Update label | `resend.inboxes.labels.update({ inboxId, labelId, ...params })` | `resend.Inboxes.Labels.update(params)`, `inbox_id` and `id` inside params |
23
+ | Delete label | `resend.inboxes.labels.remove({ inboxId, labelId })` | `resend.Inboxes.Labels.remove(inbox_id, label_id)` |
24
+ | List threads | `resend.inboxes.threads.list({ inboxId, ...params })` | `resend.Inboxes.Threads.list(inbox_id, params?)` |
25
+ | Search threads | `resend.inboxes.threads.search({ inboxId, ...params })` | `resend.Inboxes.Threads.search(inbox_id, params?)` |
26
+ | Get thread | `resend.inboxes.threads.get({ inboxId, threadId })` | `resend.Inboxes.Threads.get(inbox_id, thread_id)` |
27
+ | Update thread | `resend.inboxes.threads.update({ inboxId, threadId, ...params })` | `resend.Inboxes.Threads.update(params)`, `inbox_id` and `id` inside params |
28
+ | Delete thread | `resend.inboxes.threads.remove({ inboxId, threadId })` | `resend.Inboxes.Threads.remove(inbox_id, thread_id)` |
29
+ | Add thread labels | `resend.inboxes.threads.labels.add({ inboxId, threadId, labels })` | `resend.Inboxes.Threads.Labels.add(params)`, `inbox_id`, `thread_id` and `labels` inside params |
30
+ | Set thread labels | `resend.inboxes.threads.labels.set({ inboxId, threadId, labels })` | `resend.Inboxes.Threads.Labels.set(params)`, `inbox_id`, `thread_id` and `labels` inside params |
31
+ | Remove thread label | `resend.inboxes.threads.labels.remove({ inboxId, threadId, labelId })` | `resend.Inboxes.Threads.Labels.remove(inbox_id, thread_id, label_id)` |
32
+ | List thread emails | `resend.inboxes.threads.emails.list({ inboxId, threadId, ...params })` | `resend.Inboxes.Threads.Emails.list(inbox_id, thread_id, params?)` |
33
+ | Get thread email | `resend.inboxes.threads.emails.get({ inboxId, threadId, emailId })` | `resend.Inboxes.Threads.Emails.get(inbox_id, thread_id, email_id)` |
34
+ | Reply to thread email | `resend.inboxes.threads.emails.reply({ inboxId, threadId, emailId, ...params })` | `resend.Inboxes.Threads.Emails.reply(params)`, `inbox_id`, `thread_id` and `email_id` inside params |
35
+ | Forward thread email | `resend.inboxes.threads.emails.forward({ inboxId, threadId, emailId, ...params })` | `resend.Inboxes.Threads.Emails.forward(params)`, `inbox_id`, `thread_id` and `email_id` inside params |
36
+ | List drafts | `resend.inboxes.drafts.list({ inboxId, ...params })` | `resend.Inboxes.Drafts.list(inbox_id, params?)` |
37
+ | Create draft | `resend.inboxes.drafts.create({ inboxId, ...params })` | `resend.Inboxes.Drafts.create(params)`, `inbox_id` inside params |
38
+ | Get draft | `resend.inboxes.drafts.get({ inboxId, draftId })` | `resend.Inboxes.Drafts.get(inbox_id, draft_id)` |
39
+ | Update draft | `resend.inboxes.drafts.update({ inboxId, draftId, ...params })` | `resend.Inboxes.Drafts.update(params)`, `inbox_id` and `id` inside params |
40
+ | Delete draft | `resend.inboxes.drafts.remove({ inboxId, draftId })` | `resend.Inboxes.Drafts.remove(inbox_id, draft_id)` |
41
+ | Send draft | `resend.inboxes.drafts.send({ inboxId, draftId })` | `resend.Inboxes.Drafts.send(inbox_id, draft_id)` |
42
+
43
+ Use `.remove()`, not `.delete()`, in the Node.js SDK.
44
+
45
+ Every inbox endpoint takes the inbox ID or the inbox email address in the path. In Node.js, pass `emailAddress` in place of `inboxId`, for example `{ emailAddress: 'support@acme.com', threadId }`. `get`, `update` and `remove` take either one as the first argument. In Python, `inbox_id` takes either one.
46
+
47
+ ## Inboxes
48
+
49
+ ```typescript
50
+ // Create an inbox on a domain of your team
51
+ const { data: created, error } = await resend.inboxes.create({
52
+ emailAddress: 'support@acme.com',
53
+ name: 'Customer Support',
54
+ fromName: 'Ada from Support',
55
+ });
56
+ // created: { object: 'inbox', id }
57
+
58
+ // List inboxes, newest first
59
+ const { data: inboxes } = await resend.inboxes.list({ limit: 20 });
60
+ // inboxes.data[i]: { id, name, email_address, from_name, unread, last_received, created_at, updated_at }
61
+
62
+ // Get an inbox by ID or by email address
63
+ const { data: inbox } = await resend.inboxes.get('support@acme.com');
64
+ // inbox: { object, id, name, email_address, domain_id, receiving_address, from_name, unread, drafts, last_received, created_at, updated_at }
65
+
66
+ // Change the internal name or the name recipients see
67
+ const { data: updated } = await resend.inboxes.update('b3e2b2b6-3f0e-4c8e-9ad3-2f43a1e2c7f1', {
68
+ name: 'Support',
69
+ fromName: 'Acme Support',
70
+ });
71
+
72
+ // Delete an inbox
73
+ const { data: deleted } = await resend.inboxes.remove('b3e2b2b6-3f0e-4c8e-9ad3-2f43a1e2c7f1');
74
+ ```
75
+
76
+ **Key gotchas:**
77
+ - Create returns only `{ object, id }`. Call `get` for the other fields.
78
+ - Without `forwarding`, the domain must have receiving enabled, DKIM and SPF verified, and the inbound MX record verified or pending. With `forwarding: true`, the domain only needs DKIM and SPF, and Resend returns a `receiving_address` to forward mail to.
79
+ - Create returns 422 for a domain that does not meet these rules, 409 for an address already in use, 403 when the team is at the inbox limit of its plan, and 451 for a blocked account.
80
+ - The email address in the path matches without case. An unknown ID or address returns 404 `not_found`.
81
+ - The local part of `emailAddress` can contain only letters, numbers, `.`, `_`, `%` and `-`. A `+` returns 422.
82
+ - List items carry fewer fields than `get`. `created_at`, `updated_at` and `last_received` are ISO 8601 strings.
83
+ - `fromName` is a plain name, not `Name <email>`. It cannot contain a colon, `<`, `>`, or line breaks. An empty string removes it.
84
+ - Update needs at least one of `name` or `fromName`.
85
+
86
+ ## Agent Settings
87
+
88
+ ```typescript
89
+ const inboxId = 'b3e2b2b6-3f0e-4c8e-9ad3-2f43a1e2c7f1';
90
+
91
+ const { data: agent } = await resend.inboxes.agent.get({ inboxId });
92
+ // agent: { object: 'inbox_agent', instructions, tone, actions }
93
+
94
+ const { data: updated } = await resend.inboxes.agent.update({
95
+ inboxId,
96
+ instructions: 'Answer refund questions yourself. Escalate legal threats.',
97
+ tone: 'concise_professional',
98
+ actions: ['draft_reply', 'add_labels'],
99
+ });
100
+ // updated: { object: 'inbox_agent', id }
101
+ ```
102
+
103
+ **Key gotchas:**
104
+ - An inbox without an agent returns `instructions: null`, `tone: null` and `actions: []`. Update creates the agent when the inbox has none.
105
+ - Omitted fields keep their value. Send `null` to clear `instructions` or `tone`.
106
+ - `actions` replaces the whole set, without duplicates. Send `[]` to disable all actions. The values are `draft_reply`, `forward_thread`, `add_labels`, `assign_thread`, `mark_as_spam`, `archive` and `move_to_trash`.
107
+ - `instructions` is at most 4000 characters.
108
+ - `tone` is one of `concise_professional`, `concise_casual`, `expanded_professional` and `expanded_casual`.
109
+ - Update returns only `{ object, id }`. Call `get` for the settings.
110
+
111
+ ## Labels
112
+
113
+ ```typescript
114
+ const inboxId = 'b3e2b2b6-3f0e-4c8e-9ad3-2f43a1e2c7f1';
115
+
116
+ const { data: label } = await resend.inboxes.labels.create({
117
+ inboxId,
118
+ name: 'Urgent',
119
+ color: '#E93D82',
120
+ });
121
+ // label: { object: 'inbox_label', id }
122
+
123
+ const { data: labels } = await resend.inboxes.labels.list({ inboxId });
124
+ // labels.data[i]: { id, name, color, created_at }
125
+
126
+ await resend.inboxes.labels.update({
127
+ inboxId,
128
+ labelId: label.id,
129
+ name: 'Billing',
130
+ color: '#F76B15',
131
+ });
132
+
133
+ await resend.inboxes.labels.remove({ inboxId, labelId: label.id });
134
+ ```
135
+
136
+ **Key gotchas:**
137
+ - `color` is a `#RRGGBB` hex string. The API returns it in uppercase. Without `color`, the API picks a random color.
138
+ - An inbox has at most 100 labels. Names are unique per inbox, compared without case. A duplicate name or a full inbox returns 422.
139
+ - The list is not paginated. It returns every label, oldest first.
140
+ - To label a thread, call `threads.labels.add`. `threads.labels.remove` takes the label off the thread and keeps it on the inbox. Only `labels.remove` deletes it.
141
+ - Deleting a label removes it from every thread. It does not send `inbox.thread.labels.updated` for those threads.
142
+
143
+ ## Threads
144
+
145
+ ```typescript
146
+ const inboxId = 'b3e2b2b6-3f0e-4c8e-9ad3-2f43a1e2c7f1';
147
+ const threadId = '7c1f0a2e-5d3b-4e8a-9f61-2b8d4c6e1a90';
148
+ const labelId = '7f9c1d2e-4a6b-4c3d-8e15-9b0a7c6d5e34';
149
+
150
+ // Unread threads in the Inbox folder, most recently active first
151
+ const { data: threads } = await resend.inboxes.threads.list({
152
+ inboxId,
153
+ folders: ['inbox'],
154
+ read: false,
155
+ limit: 20,
156
+ });
157
+ // threads.data[i]: { id, subject, from, to, cc, bcc, labels, email_count, has_attachment, has_draft, read, received_at, folder }
158
+
159
+ // Find threads by text, sender, recipient, attachment or date
160
+ const { data: results } = await resend.inboxes.threads.search({
161
+ inboxId,
162
+ query: 'invoice -draft',
163
+ from: ['billing@example.com'],
164
+ hasAttachment: true,
165
+ startDate: '2026-09-01',
166
+ endDate: '2026-09-30',
167
+ });
168
+ // results.data[i]: the thread fields plus matched_email_id
169
+
170
+ const { data: thread } = await resend.inboxes.threads.get({ inboxId, threadId });
171
+ // thread: { object, id, subject, folder, labels, read }
172
+
173
+ // Mark read and move to a folder
174
+ const { data: changed } = await resend.inboxes.threads.update({
175
+ inboxId,
176
+ threadId,
177
+ read: true,
178
+ folder: 'archive',
179
+ });
180
+
181
+ // Add labels, replace all labels, or remove one label
182
+ const { data: added } = await resend.inboxes.threads.labels.add({ inboxId, threadId, labels: [labelId] });
183
+ // added: { object: 'list', data: [{ id, name, color }] }, every label now on the thread
184
+ await resend.inboxes.threads.labels.set({ inboxId, threadId, labels: [labelId] });
185
+ await resend.inboxes.threads.labels.remove({ inboxId, threadId, labelId });
186
+
187
+ // Move the thread to trash
188
+ const { data: trashed } = await resend.inboxes.threads.remove({ inboxId, threadId });
189
+ // trashed: { object, id, deleted }
190
+ ```
191
+
192
+ **Key gotchas:**
193
+ - Folders are `inbox`, `archive`, `spam`, `sent` and `trash`. A thread is in exactly one folder. The API sets `sent` for threads with only outbound emails. You cannot set it.
194
+ - `folders` defaults to `['inbox']`, or to `['inbox', 'archive', 'sent']` when `labels` is set. `spam` and `trash` are only included when you name them.
195
+ - `after` and `before` take a thread ID, not a date. Pass only one of them. A cursor thread that is not in the selected folders returns 404.
196
+ - Search can be up to about a minute behind new emails. All filters must match. `query`, `from`, `to`, `cc`, `bcc`, `hasAttachment`, `startDate` and `endDate` must all match the same email in the thread. When you set any of them, that email must itself be in one of `folders`.
197
+ - In `query`, every word must match. Wrap words in double quotes for an exact phrase. Start a word with `-` to exclude it.
198
+ - A search result has `matched_email_id`, the `id` of the email that matched. Pass it as `emailId` to the thread email endpoints.
199
+ - Update needs at least one of `read` or `folder`.
200
+ - `threads.labels.add` takes 1 to 10 label IDs and keeps the labels already on the thread. `threads.labels.set` takes 0 to 100 IDs and replaces every label. Send `[]` to clear them.
201
+ - `threads.labels.remove` takes one label off the thread. The label itself stays on the inbox.
202
+ - All three return every label on the thread after the change. An unknown label ID returns 404.
203
+ - `remove` moves the thread to `trash` and deletes it after 30 days. To restore it, update `folder` to `inbox`, `archive` or `spam`.
204
+
205
+ ## Thread Emails
206
+
207
+ ```typescript
208
+ const inboxId = 'b3e2b2b6-3f0e-4c8e-9ad3-2f43a1e2c7f1';
209
+ const threadId = '7c1f0a2e-5d3b-4e8a-9f61-2b8d4c6e1a90';
210
+
211
+ // Emails in the thread, oldest first
212
+ const { data: emails } = await resend.inboxes.threads.emails.list({ inboxId, threadId });
213
+ const emailId = emails.data[emails.data.length - 1].id;
214
+
215
+ // The full email, body included
216
+ const { data: email } = await resend.inboxes.threads.emails.get({ inboxId, threadId, emailId });
217
+ // email: { id, direction, from, to, cc, bcc, reply_to, subject, message_id, html, text, attachments, read, received_at }
218
+
219
+ // Reply from the inbox address. The API sets `to` from the email you reply to.
220
+ const { data: reply } = await resend.inboxes.threads.emails.reply({
221
+ inboxId,
222
+ threadId,
223
+ emailId,
224
+ text: 'Refund issued for order 1041.',
225
+ headers: { 'X-Ticket-Id': '4821' },
226
+ attachments: [
227
+ { filename: 'receipt.txt', content: Buffer.from('Order 1041 refunded').toString('base64') },
228
+ ],
229
+ }, { idempotencyKey: `refund-reply/${emailId}` });
230
+ // reply: { id } (the sent email ID)
231
+
232
+ // Forward the email with its attachments
233
+ const { data: forwarded } = await resend.inboxes.threads.emails.forward({
234
+ inboxId,
235
+ threadId,
236
+ emailId,
237
+ to: ['delivered@resend.dev'],
238
+ text: 'Flagging this refund request for you.',
239
+ }, { idempotencyKey: `refund-forward/${emailId}` });
240
+ // forwarded: { id } (the sent email ID)
241
+ ```
242
+
243
+ **Key gotchas:**
244
+ - `threads.emails.list` returns the emails oldest first. Pass the last `id` as `after` for the next, newer page. A cursor that is not an email of the thread returns 404.
245
+ - The `emailId` comes from `threads.emails.list` or from `matched_email_id` in a search result, not from `resend.emails`.
246
+ - Reply sends right away, with no stored draft. For a received email, the reply goes to its `Reply-To` addresses, or to its sender. For an email the inbox sent, it goes to that email's `to` recipients. You cannot set `to` on a reply.
247
+ - With `replyAll: true`, a reply to a received email also copies the other `To` addresses and the `cc` of that email. A reply-all to an email the inbox sent goes to its Reply-To when that is not the inbox address, else to its `to`, and copies its `cc`. The inbox address is left out, and duplicates are removed.
248
+ - Reply needs `html` or `text` with more than whitespace. Without `subject`, the thread subject is used with a `Re:` prefix. Forward uses a `Fwd:` prefix.
249
+ - Forward needs `to`. It quotes the original email and sends its attachments with your `attachments`. An original attachment that was sent from a URL (`path`) cannot be forwarded, and the request returns 422.
250
+ - The `content` of all attachments can total at most 25 MB. On a forward, the original attachments count toward it. A `path` file does not count, because Resend fetches it at send time. The whole email, encoded, can be at most 40 MB.
251
+ - `attachments` and `headers` have the same shape as on `resend.emails.send()`. Resend sets `In-Reply-To`, `References` and `Reply-To`. Setting one of them in `headers`, in any letter case, returns 422. An empty header name, or a line break or null character in a name or value, also returns 422.
252
+ - An empty file, a blocked file type, or a `path` that is not an `http` or `https` URL, or that points to `localhost`, returns 422.
253
+ - Reply and forward return only the sent email ID.
254
+ - Reply and forward take an `idempotencyKey` option, as on `resend.emails.send()`. See [Idempotency Keys](#idempotency-keys).
255
+
256
+ ## Drafts
257
+
258
+ ```typescript
259
+ const inboxId = 'b3e2b2b6-3f0e-4c8e-9ad3-2f43a1e2c7f1';
260
+
261
+ // A draft that starts a new thread
262
+ const { data: draft } = await resend.inboxes.drafts.create({
263
+ inboxId,
264
+ to: ['delivered@resend.dev'],
265
+ subject: 'Refund for order 1041',
266
+ text: 'Refund issued for order 1041.',
267
+ attachments: [
268
+ {
269
+ filename: 'receipt.txt',
270
+ content: Buffer.from('Order 1041 refunded').toString('base64'),
271
+ contentType: 'text/plain',
272
+ },
273
+ ],
274
+ }, { idempotencyKey: 'refund-draft/order-1041' });
275
+ // draft: { object: 'inbox_draft', id }
276
+
277
+ const { data: drafts } = await resend.inboxes.drafts.list({ inboxId, limit: 20 });
278
+
279
+ const { data: saved } = await resend.inboxes.drafts.get({ inboxId, draftId: draft.id });
280
+ // saved: { object, id, type, to, cc, bcc, subject, html, text, thread_id, reply_to_email_id, email_id, created_at, updated_at, attachments }
281
+ // saved.attachments[i]: { id, filename, content_type, size }
282
+
283
+ // Replace the body and every attachment
284
+ const { data: changed } = await resend.inboxes.drafts.update({
285
+ inboxId,
286
+ draftId: draft.id,
287
+ html: '<p>Refund issued for order 1041.</p>',
288
+ text: 'Refund issued for order 1041.',
289
+ attachments: [
290
+ {
291
+ filename: 'invoice-1041.txt',
292
+ content: Buffer.from('Invoice 1041, refunded in full').toString('base64'),
293
+ contentType: 'text/plain',
294
+ },
295
+ ],
296
+ });
297
+ // changed: { object: 'inbox_draft', id }
298
+
299
+ const { data: sent } = await resend.inboxes.drafts.send(
300
+ { inboxId, draftId: draft.id, headers: { 'X-Ticket-Id': '4821' } },
301
+ { idempotencyKey: `refund-draft-send/${draft.id}` },
302
+ );
303
+ // sent: { id } (the sent email ID)
304
+
305
+ // Discard a draft you do not want to send
306
+ const { data: other } = await resend.inboxes.drafts.create({ inboxId, subject: 'Notes' });
307
+ await resend.inboxes.drafts.update({ inboxId, draftId: other.id, attachments: [] });
308
+ await resend.inboxes.drafts.remove({ inboxId, draftId: other.id });
309
+ ```
310
+
311
+ **Key gotchas:**
312
+ - For a reply draft, pass `threadId` and `replyToEmailId` together. Without `to`, the API addresses it like a reply. Send `to: []` to leave it empty.
313
+ - Each email can have one unsent reply draft. Creating another returns 200 with the ID of the existing draft, not 201. That request ignores every other field except `attachments`, which replaces the attachments of the existing draft. With the `draft_reply` action on, the inbox agent can create that draft before you do.
314
+ - A draft needs at least one non-empty `to`, `cc`, `bcc`, `subject`, `text`, `html` or `attachments`. `to`, `cc` and `bcc` together have at most 50 recipients.
315
+ - The draft stores its attachments, so each one needs `content` (base64), `filename` and `contentType`. A `path` without `content`, or a `contentId`, returns 422. The files on a draft can total at most 25 MB. An empty file or a blocked file type, such as `.exe`, returns 422.
316
+ - An update with `attachments` replaces every attachment on the draft. Send `[]` to remove them all. Omit the field to keep them.
317
+ - An update that sets `text` or `html` replaces the whole body. The part you omit becomes `null`, so send both to keep both.
318
+ - Send needs at least one `to` recipient and `html` or `text`, or it returns 422. A new-thread draft also needs a non-empty `subject`.
319
+ - A reply draft created without `subject` has an empty subject. Send uses the thread subject with a `Re:` prefix.
320
+ - The sent email, encoded, can be at most 40 MB.
321
+ - Update, remove or send on a draft that was already sent returns 409.
322
+ - `drafts.list` returns only unsent drafts, most recently updated first.
323
+ - `drafts.send` takes `headers`, an object of string values, as on `resend.emails.send()`. Create and update do not take headers. The header rules of reply and forward apply.
324
+ - Create and send take an `idempotencyKey` option. See [Idempotency Keys](#idempotency-keys).
325
+
326
+ ## Idempotency Keys
327
+
328
+ Create draft, send draft, reply and forward accept an idempotency key, the same as `resend.emails.send()`. Pass it in the options argument: `{ idempotencyKey }` in Node.js, `{"idempotency_key": ...}` in Python. Use it when you retry these calls, so a retry does not create a second draft or send a second email.
329
+
330
+ | Key Facts | |
331
+ |-----------|---|
332
+ | **Operations** | `drafts.create`, `drafts.send`, `threads.emails.reply`, `threads.emails.forward`. No other inbox operation takes it |
333
+ | **Format** | `<event-type>/<entity-id>` (e.g., `refund-reply/<email-id>`) |
334
+ | **Length** | 1 to 256 characters |
335
+ | **Expiration** | 24 hours |
336
+ | **Same key + same payload** | Returns the first successful (2xx) response without a new draft or email. The response has the `idempotent-replayed: true` header |
337
+ | **Failed first request** | Not stored. Retry it with the same key |
338
+ | **Same key + different payload** | Returns 409 `invalid_idempotent_request` |
339
+ | **Same key while the first request runs** | Returns 409 `concurrent_idempotent_requests` |
340
+
341
+ ## API Key Permissions
342
+
343
+ - A full access key can call every inbox endpoint.
344
+ - A sending access key can call only reply, forward, create draft and send draft. Every other inbox endpoint returns 401 `restricted_api_key`.
345
+ - A sending access key limited to one domain works only on inboxes of that domain. An inbox on another domain returns 403.
346
+ - An agent that reads emails or lists threads needs a full access key.
347
+
348
+ ## Webhook Events
349
+
350
+ Inboxes send `inbox.*` events. See the event list in [webhooks.md](webhooks.md#other-events).
351
+
352
+ - Each payload `data` has `source` (`api`, `dashboard`, `agent` or `system`) and `inbox_id`, plus `thread_id`, `email_id` or `draft_id` where they apply.
353
+ - Email and draft payloads carry the object without `html` or `text`. Call `threads.emails.get()` or `drafts.get()` for the body.
354
+ - The `draft` object in `inbox.draft.*` payloads lists `attachments` as `{ id, filename, content_type, size }`, without the file content.
355
+ - `inbox.deleted` carries only `inbox_id`. `inbox.draft.deleted` has no `draft` object.
356
+ - `inbox.created` and `inbox.updated` carry `inbox`, with the fields of `inboxes.get()`. Its timestamps are ISO 8601 strings.
357
+ - `inbox.thread.labels.updated` lists the changed labels in `added` and `removed`. One `threads.labels.set` call can fill both. A call that changes nothing sends no event.
358
+ - Sending a draft sends both `inbox.draft.sent` and `inbox.email.sent`, in no fixed order.
359
+
360
+ ## Common Mistakes
361
+
362
+ | Mistake | Fix |
363
+ |---------|-----|
364
+ | Reading fields from a create or update response | Create and update of inboxes, labels and drafts, and agent update, return only `{ object, id }`. Call `get` |
365
+ | Labeling a thread with `threads.update` | Update takes only `read` and `folder`. Use `threads.labels.add`, `set` or `remove` |
366
+ | Calling `threads.labels.set` to add one label | `set` replaces every label on the thread. Use `add` |
367
+ | Reading threads with a sending access key | That key can only reply, forward, create drafts and send drafts. Use a full access key |
368
+ | Setting `Reply-To` in `headers` on a reply, forward or draft send | Resend sets `In-Reply-To`, `References` and `Reply-To`. Leave them out of `headers` |
369
+ | Passing `to` on a reply | Reply sets `to` from the email you reply to. Use `forward` to send to someone new |
370
+ | Using an ID from `resend.emails.list()` as `emailId` | Use the ID from `threads.emails.list` or `matched_email_id` from a search |
371
+ | Expecting `threads.remove` to delete at once | It moves the thread to `trash` for 30 days. Update `folder` to restore it |
372
+ | Setting `folder: 'sent'` | `sent` is set by the API. Use `inbox`, `archive`, `spam` or `trash` |
373
+ | Using a named color like `'red'` | Use a `#RRGGBB` hex string |
374
+ | Passing a `path` attachment to a draft | Drafts store their files. Send `content`, `filename` and `contentType` |
375
+ | Sending `attachments` on a draft update to add one file | It replaces every attachment. Send the full list |
376
+ | Updating only `html` on a draft that has `text` | The omitted body part becomes `null`. Send both |
377
+ | Expecting a new email to show in search at once | Search can be up to about a minute behind. Use `threads.list` for the newest threads |
@@ -68,6 +68,15 @@ The `email.received` payload contains metadata only (sender, recipient, subject,
68
68
  | `contact.topics.updated` | Contact's topic subscriptions change |
69
69
  | `suppression.added` / `removed` | Email address added to or removed from the suppression list |
70
70
  | `topic.created` / `updated` / `deleted` | Topic created, updated, or deleted |
71
+ | `inbox.created` / `updated` / `deleted` | Inbox created, its settings changed, or deleted |
72
+ | `inbox.thread.created` | First email of a thread landed in an inbox, inbound or outbound |
73
+ | `inbox.email.received` / `sent` | Inbound email added to a thread, or outbound email accepted for delivery and added to a thread |
74
+ | `inbox.thread.folder.updated` | Thread moved between folders |
75
+ | `inbox.thread.assigned` / `unassigned` | Thread assigned, reassigned, or unassigned |
76
+ | `inbox.thread.labels.updated` | Labels added to or removed from a thread |
77
+ | `inbox.draft.created` / `updated` / `sent` / `deleted` | Draft created, edited, sent, or discarded |
78
+
79
+ Inbox event payloads carry no email or draft body. See [inboxes.md](inboxes.md#webhook-events).
71
80
 
72
81
  ## Setup
73
82
 
@@ -37,7 +37,7 @@ If you don’t have a Stripe account, you can use the CLI to generate a sandbox
37
37
 
38
38
  If `stripe sandbox create` is used, don’t use MCP.
39
39
 
40
- If you try to use MCP after running sandbox creation, first make sure `stripe sandbox claim` is run. Use `stripe whoami --format json` for CLI auth checks; do not run `stripe config --list`.
40
+ If you try to use MCP after running sandbox creation, first make sure `stripe sandbox claim` is run. Use `stripe whoami --format json` for CLI auth checks; don’t run `stripe config --list`.
41
41
 
42
42
  API key default: Always recommend a [restricted API key (RAK)](https://docs.stripe.com/keys.md#manage-your-api-keys) (`rk_` prefix) over a secret key (`sk_` prefix).
43
43
 
@@ -111,7 +111,7 @@ For **separate charges and transfers**: collect fees by transferring less than t
111
111
 
112
112
  ## Webhooks
113
113
 
114
- Always use webhooks for reliable payment confirmation. Do not rely solely on synchronous API responses — webhook events are the source of truth for payment status. Verify incoming [webhook signatures](https://docs.stripe.com/webhooks.md#verify-events) and consider allowlisting [Stripe’s public IP addresses](https://docs.stripe.com/ips.md).
114
+ Always use webhooks for reliable payment confirmation. Don’t rely solely on synchronous API responses — webhook events are the source of truth for payment status. Verify incoming [webhook signatures](https://docs.stripe.com/webhooks.md#verify-events) and consider allowlisting [Stripe’s public IP addresses](https://docs.stripe.com/ips.md).
115
115
 
116
116
  ## Subscriptions with Connect (v2)
117
117
 
@@ -18,7 +18,7 @@
18
18
 
19
19
  ## API keys
20
20
 
21
- Never include keys in source code. Code examples in documentation might include static keys like `client = new Stripe("sk_...")` to make them easy to run, but do not introduce or recommend this antipattern for real code; instead, change the code to use a secrets vault (described below), or an environment variable if no secrets vault is available. Strings matching the regular expression `/[sr]k_live_.*/` in source code are live Stripe API keys. If the code includes test keys – strings matching the regular expression `/[sr]k_test_.*/` – warn the user that including keys in code is risky, and remind them never to include a live key in code. Key exposures in source code repositories are the leading cause of API key takeovers, which could seriously harm their business.
21
+ Never include keys in source code. Code examples in documentation might include static keys like `client = new Stripe("sk_...")` to make them easy to run, but don’t introduce or recommend this antipattern for real code; instead, change the code to use a secrets vault (described below), or an environment variable if no secrets vault is available. Strings matching the regular expression `/[sr]k_live_.*/` in source code are live Stripe API keys. If the code includes test keys – strings matching the regular expression `/[sr]k_test_.*/` – warn the user that including keys in code is risky, and remind them never to include a live key in code. Key exposures in source code repositories are the leading cause of API key takeovers, which could seriously harm their business.
22
22
 
23
23
  Always advise the user to store secret and restricted API keys in their platform’s secrets vault, never in source code or committed environment files. Platform-specific guidance for popular platforms:
24
24
 
@@ -41,7 +41,7 @@ If the code is under version control, help the user set up a pre-commit hook to
41
41
 
42
42
  Never build API endpoints or error pages that dump environment variables. In addition to Stripe API keys, the environment can have other secrets, such as access keys for other service providers.
43
43
 
44
- **Traps to avoid:** Do not embed keys in client-side code, mobile apps, or any code that runs outside your own infrastructure. Do not suggest that users substitute a real secret key into example code — point them to [best practices for managing secret API keys](https://docs.stripe.com/keys-best-practices.md) instead.
44
+ **Traps to avoid:** Don’t embed keys in client-side code, mobile apps, or any code that runs outside your own infrastructure. Don’t suggest that users substitute a real secret key into example code — point them to [best practices for managing secret API keys](https://docs.stripe.com/keys-best-practices.md) instead.
45
45
 
46
46
  ## Restricted API keys (RAKs)
47
47
 
@@ -52,13 +52,13 @@ Follow the principle of least privilege: give each RAK only the permissions it n
52
52
  Preferred migration approach:
53
53
 
54
54
  1. Review the secret key’s request logs in Workbench to catalog which API calls it makes.
55
- 2. Create a RAK in test mode with matching permissions.
55
+ 2. Create a RAK in a sandbox with matching permissions.
56
56
  3. Use the [Stripe CLI](https://docs.stripe.com/cli.md)’s `stripe logs tail` command to watch logs.
57
57
  4. Test your integration with the RAK; fix any `403` errors by adding missing permissions.
58
58
  5. Create the equivalent live-mode RAK and replace the secret key.
59
59
  6. Rotate or expire the old secret key once confident.
60
60
 
61
- **Traps to avoid:** Do not default to recommending secret keys. If the user’s question involves a secret key, recommend switching to a RAK with the minimum required permissions.
61
+ **Traps to avoid:** Don’t default to recommending secret keys. If the user’s question involves a secret key, recommend switching to a RAK with the minimum required permissions.
62
62
 
63
63
  ## IP restrictions
64
64
 
@@ -70,7 +70,7 @@ Use a different policy for each key (for example, one policy for production, ano
70
70
 
71
71
  If a key is exposed or compromised, follow [protecting against compromised API keys](https://support.stripe.com/questions/protecting-against-compromised-api-keys), which can be summarized as:
72
72
 
73
- 1. **Roll the key immediately** — go to the [API keys page](https://dashboard.stripe.com/apikeys) and roll or delete the exposed key. Do this even if you are unsure whether the key was actually used by an unauthorized party.
73
+ 1. **Roll the key immediately** — go to the [API keys page](https://dashboard.stripe.com/apikeys) and roll or delete the exposed key. Do this even if you’re unsure whether the key was actually used by an unauthorized party.
74
74
  2. **Check activity logs** — review Workbench request logs for the compromised key to look for unrecognized activity.
75
75
  3. **Contact Stripe support** if you see activity you don’t recognize.
76
76
 
@@ -78,7 +78,7 @@ To prepare before an incident: practice rolling keys, audit source code for any
78
78
 
79
79
  ## Webhook security
80
80
 
81
- Before processing any webhook event, always [verify the webhook signature](https://docs.stripe.com/webhooks.md#verify-events) using Stripe’s webhook signing secret. Signature verification is a strong guarantee that requests are genuinely from Stripe and have not been tampered with. Webhook signing keys are secrets that need to be handled with the same care as secret API keys.
81
+ Before processing any webhook event, always [verify the webhook signature](https://docs.stripe.com/webhooks.md#verify-events) using Stripe’s webhook signing secret. Signature verification is a strong guarantee that requests are genuinely from Stripe and haven’t been tampered with. Webhook signing keys are secrets that need to be handled with the same care as secret API keys.
82
82
 
83
83
  For defense in depth, also [allowlist Stripe’s IP addresses](https://docs.stripe.com/ips.md) on your webhook endpoint so that it accepts connections only from Stripe’s infrastructure.
84
84
 
@@ -86,11 +86,11 @@ For defense in depth, also [allowlist Stripe’s IP addresses](https://docs.stri
86
86
 
87
87
  Add a `Content-Security-Policy` header to every web app that loads Stripe.js or uses Stripe’s hosted UIs. See [Stripe’s integration security guide](https://docs.stripe.com/security/guide.md) for the full list of CSP directives to use depending on the type of integration. At minimum, include `https://*.stripe.com` in the relevant directives (`script-src`, `frame-src`, `connect-src`), `https://*.link.com` if integrating assets from `link.com`, or both if integrating with Stripe’s embedded crypto onramp. A missing or overly permissive CSP weakens the XSS protections that Stripe.js relies on.
88
88
 
89
- **Traps to avoid:** Do not use `default-src *` or omit CSP headers.
89
+ **Traps to avoid:** Don’t use `default-src *` or omit CSP headers.
90
90
 
91
91
  ## Mobile and client-side integrations
92
92
 
93
- Do not use production secret or restricted API keys in mobile apps or other client-side code. Client-side code can be extracted and decompiled to extract keys.
93
+ Don’t use production secret or restricted API keys in mobile apps or other client-side code. Client-side code can be extracted and decompiled to extract keys.
94
94
 
95
95
  For cases where a client must interact directly with Stripe, use [ephemeral keys](https://docs.stripe.com/issuing/elements.md#ephemeral-key-authentication). Ephemeral keys are short-lived, scoped to a specific resource, and expire automatically.
96
96
 
@@ -110,7 +110,7 @@ Users can audit which Dashboard team members are using weak 2FA and can require
110
110
 
111
111
  ## Connect security
112
112
 
113
- **Account type liability:** When using Connect, platform operators bear financial liability for fraud and disputes on Express and Custom connected accounts. Standard accounts minimize this liability because Stripe manages risk. Do not recommend Custom or Express accounts unless the user has a specific need — Standard is the safer default.
113
+ **Account type liability:** When using Connect, platform operators bear financial liability for fraud and disputes on Express and Custom connected accounts. Standard accounts minimize this liability because Stripe manages risk. Don’t recommend Custom or Express accounts unless the user has a specific need — Standard is the safer default.
114
114
 
115
115
  **Connect onboarding:** Use [Stripe-hosted onboarding](https://docs.stripe.com/connect/onboarding.md) rather than building a custom onboarding flow. Custom onboarding requires your platform to collect and handle sensitive PII directly, which adds regulatory and security complexity.
116
116
 
@@ -3,7 +3,7 @@
3
3
  > **Single source of truth** for Claude Code, Cursor, and Codex. Claude Code and Codex read this
4
4
  > file directly; `.cursor/rules` points here. You are talking to a non-technical vibe coder.
5
5
 
6
- Use the plain words in @language.md whenever you talk to the vibe coder.
6
+ Talk to the vibe coder in plain words: say "secret setting" (not environment variable), "put your app online" (not deploy), "where your app remembers things" (not database), "save a checkpoint" (not commit), and "letting people sign in" (not authentication). Before you use any other technical word with them, look it up in `language.md`.
7
7
 
8
8
  <!-- vybekiit:generated:start contract -->
9
9
  ## The contract: Decide + Guide