Locking fields
A locked field is rendered without an edit control, with your reason beside it. Use it when a value has already been decided elsewhere and re-choosing it inside the widget would be a second, confusing chance to disagree.
There are two ways to lock a field, and they differ in one respect that matters more than any other: whether the server refuses a booking that contradicts them.
| Where it is set | The guest sees it fixed | The server refuses a contradicting booking | |
|---|---|---|---|
| Unsigned | setLocks(), data-locked, ?locked= | Yes | No |
| Signed | locked inside the identity assertion | Yes | Yes |
Both are drawn identically. The guest cannot tell them apart, and does not need to.
Unsigned locks
Section titled “Unsigned locks”The everyday case. Nothing to sign, no backend involved.
<script src="…/widget.js" data-slug="chez-marie" data-locked="date,party_size" data-lock-reason="Gift voucher BC-4471" async></script>widget.setLocks({ fields: ["date", "partySize"], reason: "Gift voucher BC-4471" });https://book.useservice.app/r/chez-marie?date=2026-09-03&locked=date&lock_reason=Bon%20cadeaureason is the why, not a whole sentence — the widget adds its own localized
”· date fixed” per field, so “Gift voucher BC-4471” reads correctly in all
eleven languages.
Field names accept either spelling: party_size or partySize, section_key
or sectionKey, shift_id or shiftId. Unknown names are ignored.
What “unsigned” means in practice
Section titled “What “unsigned” means in practice”These travel in your HTML or in a query string, so anything can change them. The booking endpoint cannot tell an unsigned lock from no lock at all, and accepts a booking that contradicts one.
That is a deliberate trade. Requiring a signed token merely to grey out a field would be disproportionate for the common case, which is simply reflecting a choice the guest already made on your own page.
The risk worth naming is not a guest editing a URL — that is the case everyone imagines, and it barely matters. It is reading the word “locked” and concluding the booking cannot land anywhere else. It can.
Signed locks
Section titled “Signed locks”When the value must actually hold — a voucher valid on one date only, a prepaid
slot, a table your own system has already committed — put the field in locked
inside the signed token:
{ "iss": "ivk_…", "reservation": { "date": "2026-09-03", "party_size": 4 }, "locked": ["date", "party_size"]}Now the server has the values and the constraint from a source the browser cannot forge, and it refuses a create that contradicts them. The widget also positions the funnel from those same signed values, so what the guest is shown and what the server will accept cannot drift apart.
See Identifying the guest for minting.
Using both
Section titled “Using both”They combine, and the union is locked. A page can pin the party size unsigned while the token pins the date — the guest sees both fixed, and only the date is enforced.
A refused token (expired, or a bad signature) contributes nothing, and your unsigned locks are unaffected: they never depended on it.
Which fields can be locked
Section titled “Which fields can be locked”date, time, party_size, section_key, shift_id.
Contact details cannot be locked. A guest can always correct their own name, e-mail and phone — see Identifying the guest for why an asserted field stops being asserted the moment it is edited.
When nothing is available
Section titled “When nothing is available”Locking a date on which the restaurant has nothing free produces a dead end by
construction: the guest cannot change the date, and there is nothing to book. The
widget says so plainly rather than showing an empty calendar, and offers your
return_url as the way out if you supplied one. It is worth supplying one for
exactly this reason.