Skip to content

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 setThe guest sees it fixedThe server refuses a contradicting booking
UnsignedsetLocks(), data-locked, ?locked=YesNo
Signedlocked inside the identity assertionYesYes

Both are drawn identically. The guest cannot tell them apart, and does not need to.

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%20cadeau

reason 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.

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.

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.

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.

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.

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.