Repository navigation
Email the owner when Etsy approves a pending app - #1440
Merged
Merged
Conversation
In 30 days 449 Cloud workspaces were told Etsy does not accept their new app's keys yet (the app stays Pending until Etsy reviews it), and only a third ever made an Etsy call: Etsy's approval mail comes days later and nothing brought them back. - The setup check flags a 403 of a watched adapter (Etsy) as appPending; the setup page then keeps the keys on a draft connector and says the owner will be emailed. Retries update that connector. - AppApprovalWatcherService re-checks the stored keys with the same /openapi-ping call: hourly the first day, then every 6 hours, for 14 days. State lives in connectors.config.appApprovalWatch (no schema change); the email is claimed with a conditional write and checked against the etsy_app_approved_notified event, so it is sent once per connector across restarts and replicas. - New server-only product events etsy_app_pending_seen and etsy_app_approved_notified, and the appApprovedEmail template.
…tcher # Conflicts: # packages/backend/src/audit/product-event.service.ts # packages/frontend/src/app/connectors/setup/[slug]/page.tsx # packages/frontend/src/lib/api.ts
helpcodeai
enabled auto-merge (squash)
November 2, 2026 10:50
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
In the last 30 days, 449 Cloud workspaces got the Etsy setup error "Etsy does not accept these app keys yet". A new Etsy app stays Pending until Etsy reviews it. Only 34% of those workspaces later made a successful Etsy call. Etsy's approval email arrives days later, and nothing brought people back to finish the sign-in.
With this PR, the keys are kept and checked again. When Etsy accepts them, the connector's owner gets one email with a button to finish the sign-in and a first request to try in Claude.
Changes
adapters.service.ts): for a watched adapter (APP_APPROVAL_ADAPTERS, onlyetsyfor now), a 403 fromcheckBeforeAuthorizationis flaggedappPending: true. The check is split intopingAppKeys. The new publicrecheckAppKeys(slug, baseUrl, envVars)makes the same/openapi-pingcall withx-api-key: keystring:shared_secretand returnsaccepted,refusedorunknown.connectors/setup/[slug]): onappPending, the page saves the keys on a draft connector, using the existingsave(). Drafts stay hidden from MCP until signed in. The page then shows an info box instead of the red error: "Etsy reviews new apps, usually within a few days. We'll e-mail you when it's approved; nothing else to do. Your keys are saved." It also shows Etsy's answer and a link to the connector. A retry stores the new values on that connector instead of installing a second one. If the save fails (for example, the plan limit), the page shows the error as before. Messages are in en, de and it.AppApprovalWatcherService(new,src/adapters/): a sweep every 15 minutes (setInterval, the same pattern asConnectorAuthStatusService).config.adapterSlug = 'etsy', OAuth2, created in the last 14 days, stillneeds_authorizationwith both keys stored. Per-user connectors are skipped.etsy_app_pending_seenevent from the setup checksetup_verify_failedevent for Etsy with status 403, which covers setups made before this changedoneAt), or once the keys are accepted without ever having been refused (notPending, no email).connectors.config.appApprovalWatch(pendingSince,lastCheckedAt,approvedAt,notifiedAt…). It is merged with a singlejsonbupdate, so other config keys and concurrent edits are left alone. No schema change.… WHERE config->'appApprovalWatch'->>'notifiedAt' IS NULL), so only one replica wins. Before sending, the service also checks for an existingetsy_app_approved_notifiedevent for the connector. At most one email per connector, across restarts and replicas. A failed send is not retried.appApprovedEmail,EmailService.sendAppApprovedEmail): transactional and not marketing. It is sent even withemail_marketing_opt_out, because the user asked to connect Etsy, and at most once per connector.connectors.user_id)./connectors/<id>. On Cloud the link goes through the user's social sign-in, like the other emails.https://claude.ai/new?q=Show%20my%20last%2010%20Etsy%20orders.SMTP_FROM, i.e.AnythingMCP <[email protected]>on Cloud). No new SMTP config.etsy_app_pending_seen:viaissetup(withconnectorIdwhen finishing an existing connector) orwatcher.etsy_app_approved_notified: carriesconnectorId,adapterSlug, andkindset toemailedornot_sent.Cloud vs self-hosted
AnythingMCP <[email protected]>), or through the workspace's own SMTP when it has one, the same as the connector sign-in email.FRONTEND_URL. Without SMTP, nothing is sent: the outcome is logged (… accepted the app keys; no email sent) and recorded asetsy_app_approved_notifiedwithkind: not_sent, and there are no retries.Wording for the owner (not changed here)
The Etsy 403 hint lives in
adapters/intl/etsy.json(verifyHints.403), which another branch is editing, so this PR does not touch it. The new pending wording is on the setup page instead. Suggested hint text if you want them aligned: "Etsy reviews new apps, usually within a few days. We'll e-mail you when it's approved; nothing else to do. If your app is already active, copy the Shared secret again from your app page: it must belong to this Keystring."Type
Testing
New specs:
app-approval-watch.spec.ts: backoff and stop rules, matching of pending evidence, the claude.ai link.app-approval-watcher.service.spec.ts: the service runs against an in-memory connectors table whose$executeRawmock applies the same merge and claim as the SQL. It covers:adapters.service.spec.ts:appPendingonly on a 403 of a watched adapter;recheckAppKeysreturnsaccepted,refusedorunknown.adapters.controller.spec.ts:etsy_app_pending_seenis logged withconnectorId.app-approved-etsy) runs through the existingemail-templates.spec.tschecks: hostile names, allowed hosts, no placeholders, and no price talk.connector-guided-setup.spec.ts): a Pending Etsy app keeps the keys (one import), shows the message and the connector link, does not send the user to Etsy, and a retry updates the kept connector instead of importing again.Commands and results (run in this branch, after
npm ciat the root andnpx prisma generateinpackages/backend):packages/backendnpx jest --maxWorkers=2 src/adapters/app-approval-watch src/adapters/adapters.service.spec.ts src/adapters/adapters.controller.spec.ts src/settings/email src/audit/product-eventpackages/backendnpx tsc --noEmit -p tsconfig.jsonpackages/backendnpx eslint <the 13 changed backend files>(no--fix)packages/frontendnpx tsc --noEmit -p tsconfig.jsonpackages/frontendnpx eslint src/app/connectors/setup src/lib/api.ts tests/e2e/connector-guided-setup.spec.tspackages/frontendnpx playwright test tests/e2e/connector-guided-setup.spec.ts --workers=1node scripts/validate-adapters.mjsnode --test scripts/validate-adapters.test.mjs scripts/adapter-new.test.mjs scripts/generate-llms-txt.test.mjsnode scripts/adapter-count.mjs --checknode scripts/generate-llms-txt.mjs --checkThe full backend suite was not run locally; CI runs it. During Playwright,
next devprinted a react/react-dom version-mismatch warning for_document. It is unrelated to this change and did not affect the tests.Checklist
docs/tool-definition.mddoes not documentcheckBeforeAuthorizationat all (existing gap, not changed here).npm test): targeted suites only, see aboveOpen points
setup_verify_failed403 events count as evidence.Related Issues
Follows #881 and #936 (Etsy key check before the sign-in).