Multi-use links
A magic link is single-use by default. To hand out a link that may be redeemed a bounded
number of times — a shared invite, a multi-device sign-in — pass maxUses:
$link = $issuer->issueLink($user, maxUses: 3); // redeemable three times
Why concurrent redemptions cannot exceed the limit
Each redemption decrements a remaining-uses counter in the same conditional UPDATE that
consumes the token, so concurrent redemptions can never exceed the limit — the count is
checked and decremented atomically, never read-then-written.
The behavior is proven against real PostgreSQL and MySQL row locking, not only SQLite:
consumed_at is set before the decrement in the SET list so MySQL agrees with PostgreSQL
and SQLite about what the row looked like when the condition was evaluated.
When the uses run out
Once exhausted the link behaves exactly like a spent or expired one — the same generic, enumeration-resistant failure, and the same invalid-link response you configured. A reader can never tell an exhausted link from an unknown one.
The default for every link
Set the default for every link with the max_uses config key:
'max_uses' => 1,
One-time codes are always single-use regardless of this value.