Skip to content

Email claim links to the owner, and let staff pick an existing account - #49

Merged
mykolamelnykml merged 1 commit into
mainfrom
claim-invite-email
Oct 8, 2026
Merged

mykolamelnykml merged 1 commit into
mainfrom
claim-invite-email

Conversation

@mykolamelnykml

Copy link
Copy Markdown
Contributor

Why

Creating a claim link handed staff a URL to copy into their own email client. Staff also had to retype the owner's address, even when the owner already has an account.

What

  • The link is emailed automatically. New claim_invite email (MJML, text, preview.json), sent from staff.create_claim_invite, so both the tool-page panel and the catalog_create_claim_invite MCP tool send it.
  • Sent in the request, not queued. django-q keeps task arguments in the database, and the link never expires and is otherwise stored only as its SHA-256. claim_code already follows the same rule.
  • emailed in the response. A failed send is logged and returned as emailed: false instead of failing the request. The invite exists and the panel shows the link with a warning to send it by hand. On success it says "Emailed to …".
  • Pick an existing account. GET /api/v1/catalog/staff/users?q= (staff only, 403 otherwise; active accounts matched on email or name; max 10). The minter lists matches under the email field once 2+ characters are typed, and clicking one fills in the address. The owner still accepts the invite while signed in, so the audit trail is unchanged.
  • Docs: DEVELOPMENT.md (claim links, email table, queue note) and CLAUDE.md.

make schema regenerated openapi.json and the Orval client. make backend-emails compiled body.html.

Tests

  • Backend: the link is emailed (subject, recipient, URL in both parts); a mail failure still returns the link with emailed: false; the route and MCP report emailed; account search by email or name; non-staff get 403; deactivated accounts are skipped.
  • Frontend: picking an account fills in the address that gets minted; the result shows an emailed status or a failure alert.
  • Locally: backend 725 passed, frontend 916 passed, lint and typecheck clean, schema and emails not stale.

Minting a claim link used to hand staff a URL to send themselves. Now the
server emails it to the address it was minted for, and staff can choose that
address from existing accounts instead of typing it.

- New `claim_invite` email, sent by `claims.email_claim_invite` from
  `staff.create_claim_invite`, so the staff route and the MCP tool both send
  it. It goes out inside the request, never through django-q: task arguments
  are kept in the database, and the link is a credential that never expires
  and is otherwise stored only as a hash. `claim_code` works the same way.
- The response (route and MCP) carries `emailed`. A failed send is logged and
  reported, not raised: the invite still exists and staff still hold the
  link, so the panel tells them to send it by hand.
- `GET /catalog/staff/users?q=` (staff only, active accounts, matched on
  email or name, at most 10) backs a picker under the panel's email field;
  choosing an account fills in its address. Redemption is unchanged.
- DEVELOPMENT.md and CLAUDE.md describe the email and why it is not queued.
@mykolamelnykml
mykolamelnykml merged commit 80567db into main Oct 8, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant