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

ActionDefault Expiry
accept48 hours
complete7 days (after acceptance)
view30 days

Flow

  1. Landlord assigns vendor
  2. System creates vendor_token (action=‘accept’, expires_at=+48h)
  3. Notification sent with token URL
  4. Vendor clicks link → system validates (exists, not expired, not used, not revoked)
  5. Show acceptance page with issue details
  6. 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