ADR-006: Vendor Acceptance Tokens
Status: Proposed Owner: @bilal @deen Date: 2025-12-06
Context
When a landlord assigns a vendor to an issue, the vendor receives a notification with a link to accept the job. Vendors don’t have accounts, so the link must be secure, one-time use, and auditable.
Decision
Random token in database — Simple, secure, auditable.
Schema
vendor_tokens (
id UUID PRIMARY KEY,
token TEXT NOT NULL UNIQUE, -- Random 32-char, URL-safe
issue_id UUID NOT NULL REFERENCES issues(id),
vendor_id UUID NOT NULL REFERENCES vendors(id),
organisation_id UUID NOT NULL REFERENCES organisations(id),
action token_action NOT NULL, -- accept, decline, complete, view
expires_at TIMESTAMPTZ NOT NULL,
used_at TIMESTAMPTZ,
revoked_at TIMESTAMPTZ,
used_from_ip INET,
used_user_agent TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);URL Format
https://app.ehq.tech/v/accept/{token}
Token Lifecycle
Created → Active → Used / Expired / Revoked
- Active:
expires_at > NOW(),used_at IS NULL,revoked_at IS NULL - Used:
used_at IS NOT NULL - Expired:
expires_at <= NOW() - Revoked:
revoked_at IS NOT NULL(e.g., vendor reassigned)
Expiry Policy
| Action | Default Expiry |
|---|---|
| accept | 48 hours |
| complete | 7 days (after acceptance) |
| view | 30 days |
Flow
- Landlord assigns vendor
- System creates
vendor_token(action=‘accept’, expires_at=+48h) - Notification sent with token URL
- Vendor clicks link → system validates (exists, not expired, not used, not revoked)
- Show acceptance page with issue details
- Vendor accepts → mark token used, update issue status, create ‘complete’ token
Security
- Single-use (used_at prevents replay)
- Short expiry (48h default)
- Revocable if vendor changes
- IP logging for audit
- No sensitive data in URL (opaque token)
Future Considerations
- Vendor portal — If vendors get accounts, tokens become “magic links”
- Batch acceptance — “View all” token for multiple pending jobs
- QR codes — Token encoded as QR for on-site acceptance