Skip to content

Commit 3779072

Browse files
chore(copy): support@instanode.dev → contact@instanode.dev (public contact email) (#273)
User-facing contact email changed to contact@instanode.dev everywhere it appears (error/agent_action messages, billing/checkout/plan-change copy, transactional email footer, OpenAPI descriptions + snapshot). No senders changed (sender stays noreply@instanode.dev). The /support URL is unchanged — only the email address. Co-authored-by: Claude <noreply@anthropic.com>
1 parent e04782e commit 3779072

11 files changed

Lines changed: 48 additions & 48 deletions

internal/email/email.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -553,11 +553,11 @@ func clampAttemptCount(n int) int {
553553
// TEXT column on the table, so adding a new kind never needs a migration —
554554
// the only invariant is "operators can filter by kind in the dashboard".
555555
const (
556-
EmailSendKindPaymentFailed = "payment_failed"
557-
EmailSendKindPaymentReceipt = "receipt"
558-
EmailSendKindTeamInvite = "team_invite"
559-
EmailSendKindDeletionConfirm = "deletion_confirm"
560-
EmailSendKindMagicLink = "magic_link"
556+
EmailSendKindPaymentFailed = "payment_failed"
557+
EmailSendKindPaymentReceipt = "receipt"
558+
EmailSendKindTeamInvite = "team_invite"
559+
EmailSendKindDeletionConfirm = "deletion_confirm"
560+
EmailSendKindMagicLink = "magic_link"
561561
)
562562

563563
// SendPaymentFailed sends a payment failure notification email.
@@ -728,7 +728,7 @@ Receipt
728728
729729
View your billing details: https://instanode.dev/app/billing
730730
731-
Need help? Reply to this email or contact support@instanode.dev.
731+
Need help? Reply to this email or contact contact@instanode.dev.
732732
733733
— The instanode.dev team
734734
`, headline, leadPlain, receipt.Plan, amountPlain, receipt.Period)
@@ -752,7 +752,7 @@ Need help? Reply to this email or contact support@instanode.dev.
752752
</p>
753753
<p style="margin-top:24px;color:#666;font-size:13px;">
754754
Need help? Reply to this email or contact
755-
<a href="mailto:support@instanode.dev" style="color:#444;">support@instanode.dev</a>.
755+
<a href="mailto:contact@instanode.dev" style="color:#444;">contact@instanode.dev</a>.
756756
</p>
757757
<p style="margin-top:40px;color:#666;font-size:13px;">— The instanode.dev team</p>
758758
</body>

internal/handlers/agent_action.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ const AgentActionBindingLookupFailed = "Tell the user the platform couldn't reso
347347
// the middleware can't import handlers (cycle), so both sides keep their
348348
// own copy. The contract test asserts only one of the two copies; touching
349349
// either without the other is the regression we want CI to catch.
350-
const AgentActionAdminRequired = "Tell the user this endpoint requires platform-admin access. Ask support@instanode.dev via https://instanode.dev/support if you think this is wrong."
350+
const AgentActionAdminRequired = "Tell the user this endpoint requires platform-admin access. Ask contact@instanode.dev via https://instanode.dev/support if you think this is wrong."
351351

352352
// newAgentActionAdminTierChanged is returned in the success response of
353353
// POST /api/v1/admin/customers/:team_id/tier so the calling agent has

internal/handlers/billing.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -997,7 +997,7 @@ func (h *BillingHandler) CreateCheckoutAPI(c *fiber.Ctx) error {
997997
// "contact sales / not yet available" message instead of telling
998998
// the user they made a typo.
999999
return respondError(c, fiber.StatusBadRequest, "tier_not_yet_available",
1000-
"The Team plan is not yet available for self-serve checkout — contact support@instanode.dev.")
1000+
"The Team plan is not yet available for self-serve checkout — contact contact@instanode.dev.")
10011001
default:
10021002
return respondError(c, fiber.StatusBadRequest, "invalid_plan", "plan must be 'hobby', 'hobby_plus', or 'pro'")
10031003
}
@@ -3141,7 +3141,7 @@ func resolveTeamFromNotes(ctx context.Context, h *BillingHandler, sub rzpSubscri
31413141
// dashboard, executed by support staff, which fires the subscription.cancelled
31423142
// webhook → handleSubscriptionCancelled in RazorpayWebhook (unchanged).
31433143
//
3144-
// The dashboard surfaces cancellation as a mailto:support@instanode.dev link,
3144+
// The dashboard surfaces cancellation as a mailto:contact@instanode.dev link,
31453145
// not as a button that calls this API.
31463146
//
31473147
// If a future internal flow (RTBF / team deletion) needs to cancel a
@@ -3461,7 +3461,7 @@ func (h *BillingHandler) ChangePlanAPI(c *fiber.Ctx) error {
34613461
// OK — fall through.
34623462
case "yearly":
34633463
return respondError(c, fiber.StatusBadRequest, "yearly_change_plan_unsupported",
3464-
"Changing to a yearly plan via /change-plan is not yet supported. Cancel and use POST /api/v1/billing/checkout with plan_frequency='yearly', or contact support@instanode.dev.")
3464+
"Changing to a yearly plan via /change-plan is not yet supported. Cancel and use POST /api/v1/billing/checkout with plan_frequency='yearly', or contact contact@instanode.dev.")
34653465
default:
34663466
return respondError(c, fiber.StatusBadRequest, "invalid_frequency",
34673467
"plan_frequency must be 'monthly' or 'yearly'")
@@ -3489,7 +3489,7 @@ func (h *BillingHandler) ChangePlanAPI(c *fiber.Ctx) error {
34893489
// happens to be set in this environment.
34903490
if target == "team" {
34913491
return respondError(c, fiber.StatusBadRequest, "tier_not_yet_available",
3492-
"The Team plan is not yet available for self-serve plan changes — contact support@instanode.dev.")
3492+
"The Team plan is not yet available for self-serve plan changes — contact contact@instanode.dev.")
34933493
}
34943494
planIDs := h.razorpayPlanIDs()
34953495
if _, ok := planIDs[target]; !ok {
@@ -3505,9 +3505,9 @@ func (h *BillingHandler) ChangePlanAPI(c *fiber.Ctx) error {
35053505
targetRank := plans.Rank(target)
35063506
if currentRank >= 0 && targetRank >= 0 && targetRank <= currentRank {
35073507
return respondErrorWithAgentAction(c, fiber.StatusBadRequest, "downgrade_not_self_serve",
3508-
"Plan downgrades are handled by support, not self-serve. Email support@instanode.dev to change to a lower tier.",
3509-
"Tell the user that downgrading to a lower plan is support-assisted. Have them email support@instanode.dev with their team and the target plan.",
3510-
"mailto:support@instanode.dev")
3508+
"Plan downgrades are handled by support, not self-serve. Email contact@instanode.dev to change to a lower tier.",
3509+
"Tell the user that downgrading to a lower plan is support-assisted. Have them email contact@instanode.dev with their team and the target plan.",
3510+
"mailto:contact@instanode.dev")
35113511
}
35123512
// (Target=team is rejected above with tier_not_yet_available — the
35133513
// 2026-06-04 CEO re-gate. Only hobby/hobby_plus/pro upgrades reach here.)

internal/handlers/billing_block_no_cancel_downgrade_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ func TestBillingBlock_NoSelfServeCancelOrDowngradeRoute(t *testing.T) {
140140
// paying team requesting a LOWER or EQUAL tier via the in-app change-plan path
141141
// is rejected with downgrade_not_self_serve and routed to support, NOT
142142
// silently dropped. Verified against billing.go:ChangePlanAPI (it returns 400
143-
// downgrade_not_self_serve + a mailto:support@instanode.dev agent_action for
143+
// downgrade_not_self_serve + a mailto:contact@instanode.dev agent_action for
144144
// any target whose rank ≤ the current tier's rank).
145145
func TestBillingBlock_ChangePlanRejectsDowngrade(t *testing.T) {
146146
if billingBlockSkipNoDB(t) {

internal/handlers/error_envelope_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ func TestErrorEnvelope_500_NoRetryAfter(t *testing.T) {
236236
assert.NotEmpty(t, body["agent_action"],
237237
"5xx must always carry an agent_action — registry entry preferred, fallback as floor")
238238
if action, ok := body["agent_action"].(string); ok {
239-
assert.Contains(t, action, "support@instanode.dev",
239+
assert.Contains(t, action, "contact@instanode.dev",
240240
"every 5xx agent_action — whether registry or fallback — names the support contact")
241241
}
242242
}
@@ -336,7 +336,7 @@ func TestErrorEnvelope_ContactSupportContract(t *testing.T) {
336336
assert.Contains(t, s, "request_id",
337337
"AgentActionContactSupport must name request_id so the user knows what to quote")
338338
// 3. Exact next action.
339-
assert.Contains(t, s, "support@instanode.dev",
339+
assert.Contains(t, s, "contact@instanode.dev",
340340
"AgentActionContactSupport must name the support email — that's the action")
341341
// 4. Full https URL.
342342
assert.Contains(t, s, "https://instanode.dev/",

internal/handlers/helpers.go

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ type errorCodeMeta struct {
8181
// Used by respondError when status >= 500 and the code is not in the
8282
// registry. Keeps the agent_action field populated even for plumbing
8383
// errors so the calling agent always has something concrete to relay.
84-
const AgentActionContactSupport = "Tell the user something on our side went wrong. Email support@instanode.dev with this request_id and a brief description — see https://instanode.dev/support."
84+
const AgentActionContactSupport = "Tell the user something on our side went wrong. Email contact@instanode.dev with this request_id and a brief description — see https://instanode.dev/support."
8585

8686
// codeToAgentAction maps respondError `code` values to the sentence the
8787
// agent should surface and, where relevant, the upgrade URL. Codes absent
@@ -152,7 +152,7 @@ var codeToAgentAction = map[string]errorCodeMeta{
152152
// right "not yet available / contact sales" message. Refs: memory
153153
// `project_team_plan_not_rolled_out_no_payment`.
154154
"tier_not_yet_available": {
155-
AgentAction: "Tell the user the Team plan is not yet available for self-serve purchase. They should contact support@instanode.dev — see https://instanode.dev/pricing.",
155+
AgentAction: "Tell the user the Team plan is not yet available for self-serve purchase. They should contact contact@instanode.dev — see https://instanode.dev/pricing.",
156156
UpgradeURL: "https://instanode.dev/pricing",
157157
},
158158
"events_query_failed": {
@@ -288,7 +288,7 @@ var codeToAgentAction = map[string]errorCodeMeta{
288288
AgentAction: "Tell the user this invitation has already been accepted — they're on the team. Have them open https://instanode.dev/app to see their resources.",
289289
},
290290
"already_claimed": {
291-
AgentAction: "Tell the user these resources were already claimed by another account. If they believe this is wrong, have them email support@instanode.dev — see https://instanode.dev/support.",
291+
AgentAction: "Tell the user these resources were already claimed by another account. If they believe this is wrong, have them email contact@instanode.dev — see https://instanode.dev/support.",
292292
},
293293

294294
// ── Expired / gone ─────────────────────────────────────────────────────
@@ -663,7 +663,7 @@ var codeToAgentAction = map[string]errorCodeMeta{
663663
AgentAction: "Tell the user their session belongs to a different team than the storage token. Re-authenticate as the token's owning team — see https://instanode.dev/docs/auth.",
664664
},
665665
"env_load_failed": {
666-
AgentAction: "Tell the user the persisted environment variables could not be loaded for this stack. Retry the redeploy in 30 seconds — see https://instanode.dev/status. If it keeps failing, email support@instanode.dev with the request_id.",
666+
AgentAction: "Tell the user the persisted environment variables could not be loaded for this stack. Retry the redeploy in 30 seconds — see https://instanode.dev/status. If it keeps failing, email contact@instanode.dev with the request_id.",
667667
},
668668
"invalid_service": {
669669
AgentAction: "Tell the user the service value is unknown. Use one of: postgres, redis, mongodb, queue, storage, webhook, vector — see https://instanode.dev/docs.",
@@ -876,7 +876,7 @@ var codeToAgentAction = map[string]errorCodeMeta{
876876
AgentAction: "Tell the user billing is not configured on this deployment. Operators must set RAZORPAY_KEY_ID / SECRET — see https://instanode.dev/docs/billing.",
877877
},
878878
"downgrade_not_self_serve": {
879-
AgentAction: "Tell the user downgrades and cancellations are not self-serve. Email support@instanode.dev — see https://instanode.dev/support.",
879+
AgentAction: "Tell the user downgrades and cancellations are not self-serve. Email contact@instanode.dev — see https://instanode.dev/support.",
880880
},
881881
"yearly_change_plan_unsupported": {
882882
AgentAction: "Tell the user yearly subscriptions can't switch plans inline. Cancel the current subscription, then start the new plan at https://instanode.dev/pricing.",
@@ -888,7 +888,7 @@ var codeToAgentAction = map[string]errorCodeMeta{
888888

889889
// ── Razorpay codes (kept as raw passthrough) ───────────────────────────
890890
"razorpay_error": {
891-
AgentAction: "Tell the user Razorpay returned an error completing the payment. Check the error message and retry, or contact support@instanode.dev — see https://instanode.dev/support.",
891+
AgentAction: "Tell the user Razorpay returned an error completing the payment. Check the error message and retry, or contact contact@instanode.dev — see https://instanode.dev/support.",
892892
},
893893

894894
// ── Validation 4xx: signature / state ──────────────────────────────────
@@ -939,7 +939,7 @@ var codeToAgentAction = map[string]errorCodeMeta{
939939
AgentAction: "Tell the user the platform database hit a transient error. Retry in 30 seconds with exponential backoff — see https://instanode.dev/status if it persists.",
940940
},
941941
"internal_error": {
942-
AgentAction: "Tell the user something on our side went wrong. Email support@instanode.dev with this request_id, or check https://instanode.dev/status.",
942+
AgentAction: "Tell the user something on our side went wrong. Email contact@instanode.dev with this request_id, or check https://instanode.dev/status.",
943943
},
944944
"lookup_failed": {
945945
AgentAction: "Tell the user a lookup on the platform backend timed out. Retry in 30 seconds — see https://instanode.dev/status.",
@@ -988,7 +988,7 @@ var codeToAgentAction = map[string]errorCodeMeta{
988988
AgentAction: "Tell the user creating the restore failed. Retry in 60 seconds — see https://instanode.dev/status.",
989989
},
990990
"restore_failed": {
991-
AgentAction: "Tell the user the restore did not complete. Retry in 60 seconds; if it persists email support@instanode.dev — see https://instanode.dev/status.",
991+
AgentAction: "Tell the user the restore did not complete. Retry in 60 seconds; if it persists email contact@instanode.dev — see https://instanode.dev/status.",
992992
},
993993
"deletion_request_failed": {
994994
AgentAction: "Tell the user the team-deletion request failed to persist. Retry in 30 seconds — see https://instanode.dev/status.",
@@ -1000,10 +1000,10 @@ var codeToAgentAction = map[string]errorCodeMeta{
10001000
AgentAction: "Tell the user recording the promote rejection failed. Retry the rejection in 30 seconds — see https://instanode.dev/status.",
10011001
},
10021002
"execute_failed": {
1003-
AgentAction: "Tell the user executing the action failed. Retry in 30 seconds; if it persists email support@instanode.dev — see https://instanode.dev/support.",
1003+
AgentAction: "Tell the user executing the action failed. Retry in 30 seconds; if it persists email contact@instanode.dev — see https://instanode.dev/support.",
10041004
},
10051005
"summary_failed": {
1006-
AgentAction: "Tell the user computing the summary failed. Retry in 30 seconds; if it persists email support@instanode.dev — see https://instanode.dev/support.",
1006+
AgentAction: "Tell the user computing the summary failed. Retry in 30 seconds; if it persists email contact@instanode.dev — see https://instanode.dev/support.",
10071007
},
10081008
"status_failed": {
10091009
AgentAction: "Tell the user reading the status failed. Retry in 30 seconds — see https://instanode.dev/status.",
@@ -1012,13 +1012,13 @@ var codeToAgentAction = map[string]errorCodeMeta{
10121012
AgentAction: "Tell the user reading the resource status failed. Retry in 30 seconds — see https://instanode.dev/status.",
10131013
},
10141014
"tier_failed": {
1015-
AgentAction: "Tell the user updating the tier failed. Retry in 30 seconds; if it persists email support@instanode.dev — see https://instanode.dev/support.",
1015+
AgentAction: "Tell the user updating the tier failed. Retry in 30 seconds; if it persists email contact@instanode.dev — see https://instanode.dev/support.",
10161016
},
10171017
"upgrade_failed": {
1018-
AgentAction: "Tell the user the tier upgrade could not be applied right now. Retry in 30 seconds; if it persists email support@instanode.dev — see https://instanode.dev/support.",
1018+
AgentAction: "Tell the user the tier upgrade could not be applied right now. Retry in 30 seconds; if it persists email contact@instanode.dev — see https://instanode.dev/support.",
10191019
},
10201020
"revocation_failed": {
1021-
AgentAction: "Tell the user revoking the session failed. Retry in 30 seconds; if it persists email support@instanode.dev — see https://instanode.dev/support.",
1021+
AgentAction: "Tell the user revoking the session failed. Retry in 30 seconds; if it persists email contact@instanode.dev — see https://instanode.dev/support.",
10221022
},
10231023
"role_lookup_failed": {
10241024
AgentAction: "Tell the user a team-role lookup failed. Retry in 30 seconds — see https://instanode.dev/status.",
@@ -1027,13 +1027,13 @@ var codeToAgentAction = map[string]errorCodeMeta{
10271027
AgentAction: "Tell the user a team lookup failed. Retry in 30 seconds — see https://instanode.dev/status.",
10281028
},
10291029
"team_creation_failed": {
1030-
AgentAction: "Tell the user creating the team failed. Retry in 30 seconds; if it persists email support@instanode.dev — see https://instanode.dev/support.",
1030+
AgentAction: "Tell the user creating the team failed. Retry in 30 seconds; if it persists email contact@instanode.dev — see https://instanode.dev/support.",
10311031
},
10321032
"team_has_no_users": {
10331033
AgentAction: "Tell the user this team has no users yet — add an owner before issuing operations against it. See https://instanode.dev/docs/team.",
10341034
},
10351035
"user_creation_failed": {
1036-
AgentAction: "Tell the user creating the user account failed. Retry in 30 seconds; if it persists email support@instanode.dev — see https://instanode.dev/support.",
1036+
AgentAction: "Tell the user creating the user account failed. Retry in 30 seconds; if it persists email contact@instanode.dev — see https://instanode.dev/support.",
10371037
},
10381038
"user_upsert_failed": {
10391039
AgentAction: "Tell the user upserting the user record failed. Retry in 30 seconds — see https://instanode.dev/status.",
@@ -1061,10 +1061,10 @@ var codeToAgentAction = map[string]errorCodeMeta{
10611061
},
10621062
// (deletion_token_invalid covered in the deletion-confirmed section above)
10631063
"encryption_failed": {
1064-
AgentAction: "Tell the user the encryption step failed. Retry in 30 seconds; if it persists email support@instanode.dev with this request_id — see https://instanode.dev/support.",
1064+
AgentAction: "Tell the user the encryption step failed. Retry in 30 seconds; if it persists email contact@instanode.dev with this request_id — see https://instanode.dev/support.",
10651065
},
10661066
"decrypt_failed": {
1067-
AgentAction: "Tell the user decrypting the stored credential failed. Retry in 30 seconds; if it persists email support@instanode.dev with this request_id — see https://instanode.dev/support.",
1067+
AgentAction: "Tell the user decrypting the stored credential failed. Retry in 30 seconds; if it persists email contact@instanode.dev with this request_id — see https://instanode.dev/support.",
10681068
},
10691069
"encryption_unavailable": {
10701070
AgentAction: "Tell the user the encryption backend is temporarily unavailable. Retry in 60 seconds — see https://instanode.dev/status.",
@@ -1127,7 +1127,7 @@ var codeToAgentAction = map[string]errorCodeMeta{
11271127
AgentAction: "Tell the user the quota check failed. Retry in 30 seconds — see https://instanode.dev/status.",
11281128
},
11291129
"billing_persistence_failed": {
1130-
AgentAction: "Tell the user persisting the billing change failed. Retry the action in 30 seconds; if it persists email support@instanode.dev with this request_id — see https://instanode.dev/support.",
1130+
AgentAction: "Tell the user persisting the billing change failed. Retry the action in 30 seconds; if it persists email contact@instanode.dev with this request_id — see https://instanode.dev/support.",
11311131
},
11321132

11331133
// ── 429 rate-limited (canonical) ───────────────────────────────────────
@@ -1186,7 +1186,7 @@ var codeToAgentAction = map[string]errorCodeMeta{
11861186
AgentAction: "Tell the user marking the deletion as confirmed failed. Retry in 30 seconds — see https://instanode.dev/status.",
11871187
},
11881188
"subscription_cancel_failed": {
1189-
AgentAction: "Tell the user cancelling the Razorpay subscription failed. The team-delete is paused; email support@instanode.dev so an operator can reconcile — see https://instanode.dev/support.",
1189+
AgentAction: "Tell the user cancelling the Razorpay subscription failed. The team-delete is paused; email contact@instanode.dev so an operator can reconcile — see https://instanode.dev/support.",
11901190
},
11911191

11921192
// ── Auth content-type gate (AUTH-163, CSRF). Per-IP rate-limit (AUTH-097/107)

0 commit comments

Comments
 (0)