<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:fh="http://purl.org/syndication/history/1.0" xmlns:nimpress="https://github.com/nimling/nimpress">
  <channel>
    <title>Changelog</title>
    <link>https://developer.samna.io/solutions/bookable/changelog</link>
    <description>Samna developer documentation</description>
    <atom:link href="https://developer.samna.io/solutions/bookable/changelog/rss.xml" rel="self" type="application/rss+xml"/>
    <atom:link href="https://developer.samna.io/solutions/bookable/changelog/rss-2.xml" rel="prev-archive" type="application/rss+xml"/>
    <lastBuildDate>Thu, 06 Aug 2026 00:00:00 GMT</lastBuildDate>
    <ttl>3147</ttl>
    <sy:updatePeriod>weekly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <nimpress:releaseCadence>188800</nimpress:releaseCadence>
    <nimpress:latestVersion>1.12.0</nimpress:latestVersion>
    <nimpress:releaseCount>28</nimpress:releaseCount>
    <item>
      <title>v1.12.0 User identifiers and booking lists</title>
      <link>https://developer.samna.io/solutions/bookable/changelog#v1.12.0</link>
      <guid isPermaLink="true">https://developer.samna.io/solutions/bookable/changelog#v1.12.0</guid>
      <pubDate>Thu, 06 Aug 2026 00:00:00 GMT</pubDate>
      <description>Address users by your own reference on every user endpoint, and every booking list answers one shared format, window, and filter contract.</description>
      <content:encoded><![CDATA[<p>Addressing a user no longer requires the platform id, and every endpoint that lists bookings now speaks one contract. A route under <code>/api/user/{id}</code> accepts the identifier you already hold, and each booking list takes the same format, window, status, and embedding parameters wherever it lives.</p>
<h2 id="one-identifier-three-forms" tabindex="-1"><a class="header-anchor" href="#one-identifier-three-forms"><span>One identifier, three forms</span></a></h2>
<p>The <code>{id}</code> segment on every user route takes any of three forms, resolved in this order:</p>
<table>
<thead>
<tr>
<th>Form</th>
<th>What it is</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>external_id</code></td>
<td>The reference your own system supplied when the user was created or synced</td>
</tr>
<tr>
<td><code>user_id</code></td>
<td>The stable platform id carried on every user payload</td>
</tr>
<tr>
<td><code>id</code></td>
<td>The record id returned alongside <code>user_id</code> on user payloads</td>
</tr>
</tbody>
</table>
<p>This applies to reads, writes and deletes on the user itself and to every route beneath it, including <code>settings</code>, <code>claim</code>, <code>organization</code>, <code>bookable</code>, <code>booking</code>, <code>code</code>, <code>location</code>, <code>asset</code>, <code>timeslot</code>, <code>search</code>, <code>available</code> and <code>audit</code>. Responses keep carrying the canonical <code>user_id</code>, so a call made with an external reference still returns the stable id to store. An identifier that matches no user answers <code>404</code> on the routes beneath the user. See the <a href="/solutions/bookable/concepts/access">access concept</a> for how user access is resolved.</p>
<h2 id="one-booking-list-contract" tabindex="-1"><a class="header-anchor" href="#one-booking-list-contract"><span>One booking list contract</span></a></h2>
<p><code>GET /api/booking</code>, <code>/api/me/booking</code>, <code>/api/user/{id}/booking</code>, <code>/api/organization/{id}/booking</code>, <code>/api/location/{id}/booking</code>, <code>/api/bookable/{id}/booking</code> and <code>/api/bookable_type/{id}/booking</code> share one parameter set:</p>
<table>
<thead>
<tr>
<th>Parameter</th>
<th>Effect</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>format</code></td>
<td>Row shape, defaults to <code>schedule</code></td>
</tr>
<tr>
<td><code>start_date</code>, <code>end_date</code></td>
<td>RFC3339 window, paired, required when <code>format=occurrence</code> or <code>format=vector</code></td>
</tr>
<tr>
<td><code>include_date</code></td>
<td>When true each occurrence row carries its <code>schedule</code> field</td>
</tr>
<tr>
<td><code>status</code></td>
<td>Lifecycle states to include, defaults to <code>all</code></td>
</tr>
<tr>
<td><code>with</code></td>
<td>Relations to embed per row: <code>image</code>, <code>bookable</code>, <code>member</code>, <code>user</code>, <code>owner</code>, <code>creator</code>, <code>access</code>, <code>capability</code></td>
</tr>
<tr>
<td><code>q</code></td>
<td>Case insensitive substring match on booking name and description</td>
</tr>
</tbody>
</table>
<p>The <code>format</code> values pick the row shape:</p>
<table>
<thead>
<tr>
<th>Format</th>
<th>Returns</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>schedule</code></td>
<td>One row per booking with its recurrence rules</td>
</tr>
<tr>
<td><code>occurrence</code></td>
<td>One row per occurrence inside the window</td>
</tr>
<tr>
<td><code>vector</code></td>
<td>Packed time vectors per booking inside the window</td>
</tr>
<tr>
<td><code>nimcal</code></td>
<td>The combined schedule rules</td>
</tr>
<tr>
<td><code>ical</code></td>
<td>An RFC 5545 calendar as <code>text/calendar</code></td>
</tr>
</tbody>
</table>
<p><strong>breaking</strong> A call without <code>format</code> now always answers <code>schedule</code>, one row per booking. Callers that relied on another default row shape pass <code>format</code> explicitly.</p>
<p>On row shaped responses <code>count</code> reports the rows in that response, and the pagination block carries the total match count across pages. The docs panel shows a captured example body per format on every one of these endpoints.</p>
<h2 id="a-persons-own-bookings" tabindex="-1"><a class="header-anchor" href="#a-persons-own-bookings"><span>A person's own bookings</span></a></h2>
<p><code>GET /api/user/{id}/booking</code> answers with the bookings that person is attached to, rather than everything they are allowed to see. A booking counts when they created it, when they are a member of it, or when a grant names that booking directly.</p>
<p>Booking on someone's behalf through <code>POST /api/user/{id}/booking</code> now records them on the booking, so it shows up on their list the way a booking they made themselves does.</p>
<p>Add <code>inherit=true</code> to widen the answer to every booking that person can read, including the ones reaching them through an organization or location. The default is the narrower, direct view.</p>
<table>
<thead>
<tr>
<th>Value</th>
<th>Returns</th>
</tr>
</thead>
<tbody>
<tr>
<td>omitted</td>
<td>Bookings the person created, is a member of, or is named on</td>
</tr>
<tr>
<td><code>inherit=true</code></td>
<td>Every booking the person can read</td>
</tr>
</tbody>
</table>
<p>The caller still only ever sees what their own access allows, in both modes.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.11.2 Bookings count toward availability</title>
      <link>https://developer.samna.io/solutions/bookable/changelog#v1.11.2</link>
      <guid isPermaLink="true">https://developer.samna.io/solutions/bookable/changelog#v1.11.2</guid>
      <pubDate>Tue, 28 Jul 2026 00:00:00 GMT</pubDate>
      <description>A person's availability reflects the bookings already on their name, so a proposed time is only offered when everyone is genuinely free.</description>
      <content:encoded><![CDATA[<p>A person's availability now reflects what is already on their name. A time offered to a group is a time everyone in it can actually take.</p>
<h2 id="everyones-real-calendar" tabindex="-1"><a class="header-anchor" href="#everyones-real-calendar"><span>Everyone's real calendar</span></a></h2>
<p>When <code>POST /api/bookable/{id}/available</code> projects members, each person's availability weighs both their own opening rules and the bookings they already hold. A member whose rules leave an afternoon open but who is booked inside it comes back unavailable for that afternoon.</p>
<p>The effect shows up in the aggregate answer and in the conflict list: an occurrence where one member is already committed lands in <code>conflicts</code> with that member named in <code>unavailable_member_ids</code>, and the overall <code>available</code> reads false.</p>
<p>Scheduling a booking across several people now converges on times the whole group can keep.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.11.1 Explicit availability windows</title>
      <link>https://developer.samna.io/solutions/bookable/changelog#v1.11.1</link>
      <guid isPermaLink="true">https://developer.samna.io/solutions/bookable/changelog#v1.11.1</guid>
      <pubDate>Tue, 28 Jul 2026 00:00:00 GMT</pubDate>
      <description>Name the range you care about on any availability call, or leave it out and let a proposed schedule set its own bounds.</description>
      <content:encoded><![CDATA[<p>You can now name the range an availability call covers, or leave it out and let the proposed booking set its own bounds.</p>
<h2 id="ask-for-the-range-you-want" tabindex="-1"><a class="header-anchor" href="#ask-for-the-range-you-want"><span>Ask for the range you want</span></a></h2>
<p><code>start_date</code> and <code>end_date</code> on <code>POST /api/bookable/{id}/available</code> narrow or widen the window independently of the schedule in the body. Omit either one and the range is taken from the candidate schedule instead.</p>
<table>
<thead>
<tr>
<th>Parameter</th>
<th>Omitted behavior</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>start_date</code></td>
<td>Falls back to the earliest start in the candidate schedule</td>
</tr>
<tr>
<td><code>end_date</code></td>
<td>Falls back to the latest end in the candidate schedule, when the schedule ends</td>
</tr>
</tbody>
</table>
<p>An open ended recurrence has no latest end, so a call that projects one supplies <code>end_date</code> to say how far ahead to look. Both values are RFC3339.</p>
<p>This makes a single call serve two shapes: checking one proposed booking, and asking what a resource looks like across a month you name yourself.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.11.0 Availability at a glance</title>
      <link>https://developer.samna.io/solutions/bookable/changelog#v1.11.0</link>
      <guid isPermaLink="true">https://developer.samna.io/solutions/bookable/changelog#v1.11.0</guid>
      <pubDate>Tue, 28 Jul 2026 00:00:00 GMT</pubDate>
      <description>Each availability block answers yes or no on its own, so a client can gate a button without walking the underlying windows.</description>
      <content:encoded><![CDATA[<p>Each availability block now answers on its own. A client that only needs to know whether to enable a button reads one value instead of walking the windows underneath it.</p>
<h2 id="one-value-to-gate-on" tabindex="-1"><a class="header-anchor" href="#one-value-to-gate-on"><span>One value to gate on</span></a></h2>
<p><code>availability.available</code> comes back on the bookable's block and on every projected member's block from <code>POST /api/bookable/{id}/available</code>. What it answers depends on what you asked:</p>
<table>
<thead>
<tr>
<th>Request</th>
<th><code>available</code> is true when</th>
</tr>
</thead>
<tbody>
<tr>
<td>With a candidate schedule</td>
<td>Every occurrence the schedule produces works for that bookable or member</td>
</tr>
<tr>
<td>Without a candidate schedule</td>
<td>The complete requested interval is open for that bookable or member</td>
</tr>
</tbody>
</table>
<p>The detailed windows stay exactly where they are, so a picker that renders individual slots keeps reading them while a simpler screen gates on the single value.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.10.1 Complete availability day vectors</title>
      <link>https://developer.samna.io/solutions/bookable/changelog#v1.10.1</link>
      <guid isPermaLink="true">https://developer.samna.io/solutions/bookable/changelog#v1.10.1</guid>
      <pubDate>Tue, 28 Jul 2026 00:00:00 GMT</pubDate>
      <description>Availability answers with one entry for every day in the window, so a calendar grid maps straight onto the response.</description>
      <content:encoded><![CDATA[<p>An availability answer now covers every day in the window. A calendar grid maps one cell onto one entry with no gaps to reason about.</p>
<h2 id="a-slot-for-every-day" tabindex="-1"><a class="header-anchor" href="#a-slot-for-every-day"><span>A slot for every day</span></a></h2>
<p><code>vector_days</code> on <code>POST /api/bookable/{id}/available</code> returns one entry per day between <code>start_at</code> and <code>end_at</code>. A day with nothing open comes back as an empty list rather than being left out, so the position of an entry in the array is the day it describes.</p>
<p>A client that renders a month view indexes into the array by day offset and gets the right cell every time, including for a resource that is closed for a stretch of the window.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.10.0 Candidate schedule projection</title>
      <link>https://developer.samna.io/solutions/bookable/changelog#v1.10.0</link>
      <guid isPermaLink="true">https://developer.samna.io/solutions/bookable/changelog#v1.10.0</guid>
      <pubDate>Mon, 27 Jul 2026 00:00:00 GMT</pubDate>
      <description>Send a proposed booking and every occurrence is checked against the resource and everyone invited before anything is created.</description>
      <content:encoded><![CDATA[<p>You can now try a booking before you make it. Send the schedule you have in mind together with the people you plan to invite, and the answer tells you whether every occurrence works for the resource and for everyone on the list.</p>
<h2 id="try-the-booking-first" tabindex="-1"><a class="header-anchor" href="#try-the-booking-first"><span>Try the booking first</span></a></h2>
<p><code>POST /api/bookable/{id}/available</code> accepts a candidate booking in the body. <code>schedule</code> takes one schedule or an array of them and is required. <code>members</code> carries the people to project alongside it and defaults to empty. The signed in caller is always projected, whether or not they appear in <code>members</code>.</p>
<p>Every occurrence the schedule produces inside the date range is checked against the bookable and against each projected member.</p>
<h2 id="reading-the-verdict" tabindex="-1"><a class="header-anchor" href="#reading-the-verdict"><span>Reading the verdict</span></a></h2>
<p>The response carries an aggregate answer plus the detail behind it.</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>Holds</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>available</code></td>
<td>True when every occurrence works for the bookable and for all projected members</td>
</tr>
<tr>
<td><code>occurrence_count</code></td>
<td>How many occurrences the schedule produced inside the range</td>
</tr>
<tr>
<td><code>conflicts</code></td>
<td>One entry per occurrence that does not work</td>
</tr>
<tr>
<td><code>members</code></td>
<td>The projected members, keyed by id</td>
</tr>
<tr>
<td><code>availability</code></td>
<td>Per member availability, keyed by the same ids</td>
</tr>
</tbody>
</table>
<p>Each conflict names the occurrence and who it fails for:</p>
<pre data-lang="json" class="shiki github-dark" style="background-color:#24292e;color:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#E1E4E8">{</span></span>
<span class="line"><span style="color:#79B8FF">  "occurrence_id"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"c034d0f9-6f31-4af8-bf31-30da808c0418"</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#79B8FF">  "starts_at"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"2030-06-03T10:00:00Z"</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#79B8FF">  "ends_at"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"2030-06-03T11:00:00Z"</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#79B8FF">  "bookable"</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">false</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#79B8FF">  "unavailable_member_ids"</span><span style="color:#E1E4E8">: []</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span>
<span class="line"></span></code></pre>
<p><code>bookable</code> is true when the resource itself is free for the whole occurrence, and <code>unavailable_member_ids</code> lists the projected members who are not. A recurring proposal that works on most dates comes back with the handful of dates that do not, so a client can offer to drop them or shift them rather than failing the whole request.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.9.2 Sharper availability checks</title>
      <link>https://developer.samna.io/solutions/bookable/changelog#v1.9.2</link>
      <guid isPermaLink="true">https://developer.samna.io/solutions/bookable/changelog#v1.9.2</guid>
      <pubDate>Fri, 24 Jul 2026 00:00:00 GMT</pubDate>
      <description>Availability weighs every rule that touches a bookable, so a window offered as free holds up when the booking is placed.</description>
      <content:encoded><![CDATA[<p>Availability now weighs every rule that touches a bookable before it calls a window free. A slot offered to a user holds up when the booking is placed against it.</p>
<h2 id="free-means-bookable" tabindex="-1"><a class="header-anchor" href="#free-means-bookable"><span>Free means bookable</span></a></h2>
<p><code>POST /api/bookable/{id}/available</code> resolves the complete rule set on the bookable, including the rules it draws from its parent, its location, its owner and its type, and only reports a window free when all of them agree. A window that any one rule closes is reported closed.</p>
<p>The result is that the windows a picker renders and the windows <code>POST /api/booking</code> accepts are the same set.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.9.1 Availability window bounds</title>
      <link>https://developer.samna.io/solutions/bookable/changelog#v1.9.1</link>
      <guid isPermaLink="true">https://developer.samna.io/solutions/bookable/changelog#v1.9.1</guid>
      <pubDate>Fri, 24 Jul 2026 00:00:00 GMT</pubDate>
      <description>Every availability answer states the window it was computed over, so a client can render a timeline without tracking what it asked for.</description>
      <content:encoded><![CDATA[<p>An availability answer now states the window it covers. The bounds travel with the result, so a client can lay out a timeline from the response alone.</p>
<h2 id="the-window-travels-with-the-answer" tabindex="-1"><a class="header-anchor" href="#the-window-travels-with-the-answer"><span>The window travels with the answer</span></a></h2>
<p><code>POST /api/bookable/{id}/available</code> returns <code>start_at</code> and <code>end_at</code> on every availability block, filled from the range the request resolved to. A client that renders a calendar strip reads the bounds off the payload instead of holding on to the values it sent.</p>
<p>The same bounds appear on the bookable's own block and on each projected member's block, so every lane on a timeline lines up against one shared axis.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.9.0 Member identity on bookings</title>
      <link>https://developer.samna.io/solutions/bookable/changelog#v1.9.0</link>
      <guid isPermaLink="true">https://developer.samna.io/solutions/bookable/changelog#v1.9.0</guid>
      <pubDate>Thu, 23 Jul 2026 00:00:00 GMT</pubDate>
      <description>Booking membership answers with the person behind each place, so a guest list renders without a second round of lookups.</description>
      <content:encoded><![CDATA[<p>A booking membership now tells you who the member is. Name, email, your own reference and the invite state come back on the membership itself, so a guest list renders straight from one response.</p>
<h2 id="who-holds-each-place" tabindex="-1"><a class="header-anchor" href="#who-holds-each-place"><span>Who holds each place</span></a></h2>
<p>Every membership row carries the identity resolved from the linked user, and keeps the invite details for a place that nobody has accepted yet.</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>Holds</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>first_name</code></td>
<td>Given name of the linked user</td>
</tr>
<tr>
<td><code>last_name</code></td>
<td>Family name of the linked user</td>
</tr>
<tr>
<td><code>email</code></td>
<td>The invite address on the membership, or the linked user's address once bound</td>
</tr>
<tr>
<td><code>external_id</code></td>
<td>The reference your own system supplied when the place was invited</td>
</tr>
<tr>
<td><code>status</code></td>
<td>Invite state: <code>pending</code>, <code>accepted</code> or <code>declined</code></td>
</tr>
</tbody>
</table>
<p>The fields arrive on <code>POST /api/booking/{id}/member</code>, <code>PUT /api/booking/{id}/member</code>, <code>DELETE /api/booking/{id}/member</code>, <code>DELETE /api/booking/{id}/member/{member_id}</code> and on any booking list asked for with <code>with=member</code>.</p>
<h2 id="one-call-for-a-guest-list" tabindex="-1"><a class="header-anchor" href="#one-call-for-a-guest-list"><span>One call for a guest list</span></a></h2>
<p>An invited place that no one has accepted still answers with the address and reference it was invited under, so a pending seat and a filled seat read the same shape. Asking for <code>with=user</code> alongside <code>with=member</code> still embeds the full user record when you need more than the name.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.8.1 Members on bookable listings</title>
      <link>https://developer.samna.io/solutions/bookable/changelog#v1.8.1</link>
      <guid isPermaLink="true">https://developer.samna.io/solutions/bookable/changelog#v1.8.1</guid>
      <pubDate>Wed, 15 Jul 2026 00:00:00 GMT</pubDate>
      <description>Booking lists under a bookable embed the people holding each booking, so a room schedule shows its attendees in one call.</description>
      <content:encoded><![CDATA[<p>A booking list read through a bookable now embeds the people on each booking. A room schedule renders its attendees without a follow up call per booking.</p>
<h2 id="attendees-on-a-room-schedule" tabindex="-1"><a class="header-anchor" href="#attendees-on-a-room-schedule"><span>Attendees on a room schedule</span></a></h2>
<p><code>GET /api/bookable/{id}/booking</code> honours <code>with=member</code> and returns the membership of every booking in the list. Combine it with <code>with=user</code> to embed the full user record alongside each place.</p>
<p>The behaviour matches the other booking listings, so the same client code reads a schedule whether it starts from a bookable, an organization, a location or a user.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.8.0 Activity report documents</title>
      <link>https://developer.samna.io/solutions/bookable/changelog#v1.8.0</link>
      <guid isPermaLink="true">https://developer.samna.io/solutions/bookable/changelog#v1.8.0</guid>
      <pubDate>Fri, 03 Jul 2026 00:00:00 GMT</pubDate>
      <description>Activities render as finished branded documents, and the graph endpoints gain windowed browsing with usage aware ordering.</description>
      <content:encoded><![CDATA[<p>Activity reports now come back as finished documents. The same figures render as a styled HTML page, a print ready PDF, or Markdown, and several activities combine into one report.</p>
<h2 id="graph-windowing" tabindex="-1"><a class="header-anchor" href="#graph-windowing"><span>Graph windowing</span></a></h2>
<p>The graph endpoints <code>/api/graph/owner</code>, <code>/api/graph/claim</code> and <code>/api/graph/relation</code> now bound how much of the graph one call returns, so a large tree loads one window at a time instead of all at once.</p>
<table>
<thead>
<tr>
<th>Parameter</th>
<th>Effect</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>width</code></td>
<td>Max children per node, mirroring how <code>depth</code> caps levels; <code>0</code> means unlimited</td>
</tr>
<tr>
<td><code>sort</code></td>
<td>Ordering that decides which children fill the window: <code>name</code> for alphabetical, <code>usage</code> for most used first</td>
</tr>
<tr>
<td><code>next_id</code></td>
<td>Continuation cursor naming the last child received under <code>start_id</code>; the response resumes after it</td>
</tr>
</tbody>
</table>
<p>When <code>width</code> is set the <code>objects</code> half of the response shrinks to the nodes that survived the window, so responses stay small end to end. A child cut by <code>width</code> takes its whole subtree with it, keeping every returned link connected. Fetch the first window with <code>start_id</code> and <code>width</code>, then pass the last received child as <code>next_id</code> to page through the rest.</p>
<h2 id="usage-aware-ordering" tabindex="-1"><a class="header-anchor" href="#usage-aware-ordering"><span>Usage aware ordering</span></a></h2>
<p>The platform now counts how often each signed in user opens an organization, location, bookable, bookable type, timeslot, claim, capability, action, code or asset. With <code>sort=usage</code> the graph fills each window with the objects that user opens most, falling back to alphabetical order for objects opened equally often.</p>
<h2 id="report-documents" tabindex="-1"><a class="header-anchor" href="#report-documents"><span>Report documents</span></a></h2>
<p>Add <code>/report</code> to any activity to render it. <code>GET /api/activity/{function}/report</code> returns one report and <code>GET /api/activity/report</code> returns a combined one built from several activities. The <code>format</code> parameter picks the output and defaults to <code>pdf</code>. A single activity renders as <code>html</code>, <code>pdf</code> or <code>md</code> and also returns its raw rows as <code>csv</code> or <code>json</code>; the combined report carries no single table, so it renders only as <code>html</code>, <code>pdf</code> or <code>md</code>. Pass <code>timezone</code> as an IANA name such as <code>Europe/Oslo</code> and every date and time in the document reads in that zone instead of UTC. See the <a href="/solutions/bookable/concepts/reporting">reporting concept</a> and the <a href="/solutions/bookable/examples/render-activity-reports">render activity reports example</a>.</p>
<h2 id="branded-templates" tabindex="-1"><a class="header-anchor" href="#branded-templates"><span>Branded templates</span></a></h2>
<p>An organization brands its reports with its own templates, picked over the built in ones. Upload the template as an asset and set its id on the organization settings through <code>PATCH /api/organization/{id}/settings</code>, for a single function or for the combined report, and override it per request with the <code>template</code> parameter. The asset <code>type</code> marks the role a template plays:</p>
<table>
<thead>
<tr>
<th>Type</th>
<th>Holds</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>activity_template</code></td>
<td>A report template body, <code>html</code> or <code>md</code></td>
</tr>
<tr>
<td><code>notification_template</code></td>
<td>A notification template body</td>
</tr>
</tbody>
</table>
<p>See the <a href="/solutions/bookable/concepts/asset">asset concept</a>.</p>
<h2 id="template-functions" tabindex="-1"><a class="header-anchor" href="#template-functions"><span>Template functions</span></a></h2>
<blockquote>
<p>The template format and the functions below are highly likely to change. Treat custom templates as experimental and expect to revisit them.</p>
</blockquote>
<p>A template embeds other assets by id, so logos and images live in the asset system and still print, and it runs activities and shapes tables through a small set of functions:</p>
<table>
<thead>
<tr>
<th>Function</th>
<th>What it does</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>activity &quot;name&quot; ...</code></td>
<td>Runs an activity and returns its rows; trailing key value pairs override the request parameters for that call</td>
</tr>
<tr>
<td><code>table rows ...</code>, <code>mdtable rows ...</code></td>
<td>Render rows as a table; <code>&quot;column&quot; &quot;Label&quot;</code> pairs choose and rename columns, otherwise every column shows with a readable header and id columns drop</td>
</tr>
<tr>
<td><code>countrows rows</code>, <code>sumcol rows &quot;col&quot;</code>, <code>maxcol rows &quot;col&quot;</code></td>
<td>Count, sum and maximum over a column for summary figures</td>
</tr>
<tr>
<td><code>secs2min rows &quot;col&quot; ...</code></td>
<td>Convert the named columns from seconds to minutes</td>
</tr>
<tr>
<td><code>humanize &quot;name&quot;</code></td>
<td>Turn a snake case name into a readable label</td>
</tr>
<tr>
<td><code>asset &quot;id&quot;</code>, <code>image &quot;id&quot;</code></td>
<td>Inline an asset by id as a data URI; <code>image</code> rejects a non image with <code>422</code>, an unreadable id answers <code>403</code>, a missing id <code>404</code></td>
</tr>
<tr>
<td><code>pricetier peak</code>, <code>nok n</code></td>
<td>Resolve the concurrent bookings pricing tier and format a NOK amount</td>
</tr>
</tbody>
</table>
<h2 id="concurrent-bookings-invoice" tabindex="-1"><a class="header-anchor" href="#concurrent-bookings-invoice"><span>Concurrent bookings invoice</span></a></h2>
<p>The <code>concurrent_bookings</code> report renders as a billing invoice. It takes the peak concurrent bookings in the window, matches it to its pricing tier, and lays out a NOK invoice with the billing period, the monthly charge, MVA, and the annual and discounted totals, followed by a per period breakdown. Ask for <code>pdf</code> for the printable invoice, <code>html</code> to read it inline, or <code>json</code> for the raw concurrency figures.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.7.2 Filter listings by name</title>
      <link>https://developer.samna.io/solutions/bookable/changelog#v1.7.2</link>
      <guid isPermaLink="true">https://developer.samna.io/solutions/bookable/changelog#v1.7.2</guid>
      <pubDate>Thu, 02 Jul 2026 00:00:00 GMT</pubDate>
      <description>Every resource listing can now be narrowed by name so a targeted lookup stays a single call.</description>
      <content:encoded><![CDATA[<p>Every listing of a named resource now takes a name filter, so you can fetch a specific record without paging through the whole set.</p>
<h2 id="name-filter" tabindex="-1"><a class="header-anchor" href="#name-filter"><span>Name filter</span></a></h2>
<p>Pass <code>q</code> (or <code>query</code>) to a listing to keep only the records whose name contains the value, case insensitive. It pairs with <code>limit</code> and paging, so a targeted lookup stays one request.</p>
<p>The filter is available on <code>/api/organization</code>, <code>/api/location</code>, <code>/api/bookable</code>, <code>/api/bookable_type</code>, <code>/api/timeslot</code>, <code>/api/claim</code>, <code>/api/capability</code>, <code>/api/action</code>, and <code>/api/code</code>, on the caller scoped <code>/api/me/organization</code>, <code>/api/me/location</code>, <code>/api/me/bookable</code>, <code>/api/me/timeslot</code>, <code>/api/me/code</code>, and <code>/api/me/asset</code> listings, and on their <code>/api/user/{id}/...</code> counterparts.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.7.1 Timely activity recording</title>
      <link>https://developer.samna.io/solutions/bookable/changelog#v1.7.1</link>
      <guid isPermaLink="true">https://developer.samna.io/solutions/bookable/changelog#v1.7.1</guid>
      <pubDate>Thu, 02 Jul 2026 00:00:00 GMT</pubDate>
      <description>Booking end and check in activity is now recorded promptly and reliably even while the platform scales.</description>
      <content:encoded><![CDATA[<p>When a booking ends, its activity now appears within seconds, every time, and the same holds for check in and check out windows. Scaling the platform up or down no longer delays or drops these records.</p>
<h2 id="activity-on-time" tabindex="-1"><a class="header-anchor" href="#activity-on-time"><span>Activity on time</span></a></h2>
<p><code>GET /api/activity</code> reflects lifecycle events as they happen: the <code>booking_ended</code> event lands within seconds of the booking's end, and the <code>checkin_started</code>, <code>checkin_failed</code>, <code>checkout_started</code>, and <code>checkout_failed</code> events land as their windows open and close.</p>
<h2 id="recorded-once" tabindex="-1"><a class="header-anchor" href="#recorded-once"><span>Recorded once</span></a></h2>
<p>Each event is recorded exactly once, so activity reports and utilization figures never double count a booking that ends or a check in that opens.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.7.0 Search filtering and access speed</title>
      <link>https://developer.samna.io/solutions/bookable/changelog#v1.7.0</link>
      <guid isPermaLink="true">https://developer.samna.io/solutions/bookable/changelog#v1.7.0</guid>
      <pubDate>Wed, 01 Jul 2026 00:00:00 GMT</pubDate>
      <description>Search applies every filter and exclusion in one server side pass with stable paging.</description>
      <content:encoded><![CDATA[<p>Search now resolves in one server side pass, access changes across large structures return faster, and the claim graph guards its starting point.</p>
<h2 id="search-filtering-and-scoping" tabindex="-1"><a class="header-anchor" href="#search-filtering-and-scoping"><span>Search filtering and scoping</span></a></h2>
<p><code>GET /api/search</code> applies every scope and exclusion in a single pass and returns a correct <code>total_count</code> for paging. Scope with <code>org_ids</code>, <code>loc_ids</code>, <code>claim_id</code>, <code>type_ids</code>, <code>parent_id</code>, or <code>parent_type</code>, and pare results with <code>exclude_object_types</code>, <code>exclude_org_ids</code>, <code>exclude_loc_ids</code>, <code>exclude_query</code>, <code>usr_ids</code>, and <code>exclude_usr_ids</code>. Results carry a stable order, so successive pages tile without dropping or repeating a row at the boundary.</p>
<h2 id="faster-access-changes" tabindex="-1"><a class="header-anchor" href="#faster-access-changes"><span>Faster access changes</span></a></h2>
<p>Creating or granting a claim that cascades across a large ownership or location structure returns much faster.</p>
<h2 id="claim-object-tree" tabindex="-1"><a class="header-anchor" href="#claim-object-tree"><span>Claim object tree</span></a></h2>
<p><code>GET /api/claim/{id}/object</code> lists the objects a claim is bound to. Pass <code>inherit=true</code> to also include the objects the claim reaches through cascade, each row carrying <code>inherited</code> and its <code>depth</code> in the tree. Reading the list requires read access to the claim.</p>
<h2 id="claim-graph-access" tabindex="-1"><a class="header-anchor" href="#claim-graph-access"><span>Claim graph access</span></a></h2>
<p><code>GET /api/graph/claim</code> now requires read access to the claim named by <code>start_id</code>, answering <code>404</code> for an unknown claim and <code>403</code> for one you cannot read.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.6.4 Build and deploy pipeline</title>
      <link>https://developer.samna.io/solutions/bookable/changelog#v1.6.4</link>
      <guid isPermaLink="true">https://developer.samna.io/solutions/bookable/changelog#v1.6.4</guid>
      <pubDate>Mon, 29 Jun 2026 00:00:00 GMT</pubDate>
      <description>Production deploys now run through a guided version picker with a manual approval, and the schema migration runner was advanced.</description>
      <content:encoded><![CDATA[<p>The build and deploy pipeline was modernized. A production deploy now picks an existing version through a guided prompt and waits for an explicit approval before it dispatches the build and deploy run. The migration runner version used in the pipeline was upgraded, with a set of migrations folded into the baseline and the result tested. The build, deploy, and type publishing steps and the published TypeScript client were moved onto a current Node runtime and verified to build and run cleanly on it.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.6.3 Consistent user access</title>
      <link>https://developer.samna.io/solutions/bookable/changelog#v1.6.3</link>
      <guid isPermaLink="true">https://developer.samna.io/solutions/bookable/changelog#v1.6.3</guid>
      <pubDate>Mon, 29 Jun 2026 00:00:00 GMT</pubDate>
      <description>Access you hold on a user resolves the same way across every endpoint and every kind of user, capabilities gain a free form label, and search runs leaner.</description>
      <content:encoded><![CDATA[<p>Access you hold on a user resolves the same way on every user endpoint and for every kind of user, whether the user is created directly, bridged through a token, or provisioned from an external system. Capabilities also gain a free form type label, and search runs leaner.</p>
<h2 id="user-access-across-every-endpoint" tabindex="-1"><a class="header-anchor" href="#user-access-across-every-endpoint"><span>User access across every endpoint</span></a></h2>
<p>The access you hold on a user resolves identically on single reads, listings, and bulk checks, and applies the same regardless of how the user was added. Updating a user through <code>PUT /api/user/{id}</code> or <code>PATCH /api/user/{id}</code> checks the access you hold on that user, and the <code>access</code> filter on <code>GET /api/user</code> and <code>GET /api/user/{id}</code> (<code>access=read</code>, <code>access=write</code>, <code>access=execute</code>) returns exactly the users you hold that level on.</p>
<p>The three ways to add a user behave the same for access: created directly with <code>POST /api/user</code>, bridged from a sign in token with <code>POST /api/user/token</code>, or provisioned from an external system with <code>POST /api/user/external</code>.</p>
<h2 id="capability-type-labels" tabindex="-1"><a class="header-anchor" href="#capability-type-labels"><span>Capability type labels</span></a></h2>
<p>Capabilities now carry a free form <code>type</code> label. Set it on <code>POST /api/capability</code>, <code>PUT /api/capability/{id}</code>, or <code>PATCH /api/capability/{id}</code>, and it is returned on every capability response.</p>
<h2 id="organization-updates" tabindex="-1"><a class="header-anchor" href="#organization-updates"><span>Organization updates</span></a></h2>
<p>You can update an organization without restating its type. A <code>PUT /api/organization/{id}</code> that leaves out <code>type</code> keeps the current value, so an organization stays editable through the API.</p>
<h2 id="leaner-search" tabindex="-1"><a class="header-anchor" href="#leaner-search"><span>Leaner search</span></a></h2>
<p>Full text search was corrected and optimized with a faster access lookup, so results resolve consistently with your access and return faster.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.6.2 Activity report scoping</title>
      <link>https://developer.samna.io/solutions/bookable/changelog#v1.6.2</link>
      <guid isPermaLink="true">https://developer.samna.io/solutions/bookable/changelog#v1.6.2</guid>
      <pubDate>Tue, 23 Jun 2026 00:00:00 GMT</pubDate>
      <description>Activity reports can now be aimed at the objects you choose, default to everything you can see, and run leaner across the board.</description>
      <content:encoded><![CDATA[<p>Activity reports can now be aimed at the objects you care about. Point a report at an organization, location, or bookable and it narrows to everything beneath that object; leave the anchor off and the report covers every object you can see. The reporting functions were streamlined and run faster in this release.</p>
<h2 id="scoping-a-report-to-an-object" tabindex="-1"><a class="header-anchor" href="#scoping-a-report-to-an-object"><span>Scoping a report to an object</span></a></h2>
<p>The usage reports under <code>GET /api/activity/{func}</code> accept an <code>object</code> parameter written as <code>object_type:object_id</code>. Each anchor resolves through ownership, location, and parenthood, so a single organization or location pulls in every bookable beneath it.</p>
<table>
<thead>
<tr>
<th>Report</th>
<th>Object scoping</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>total_bookings_by_period</code></td>
<td>Accepts <code>object</code> to scope the report to chosen objects</td>
</tr>
<tr>
<td><code>most_popular_period</code></td>
<td>Accepts <code>object</code> to scope the report to chosen objects</td>
</tr>
<tr>
<td><code>concurrent_bookings</code></td>
<td><code>object</code> is now optional</td>
</tr>
</tbody>
</table>
<p>Pass more than one <code>object</code> to combine anchors, for example <code>object=organization:...&amp;object=location:...</code>.</p>
<h2 id="reports-default-to-everything-you-can-see" tabindex="-1"><a class="header-anchor" href="#reports-default-to-everything-you-can-see"><span>Reports default to everything you can see</span></a></h2>
<p>When you leave <code>object</code> off, a report covers every object your access lets you read. The <code>concurrent_bookings</code> report no longer requires an anchor; without one it spans all the bookables you can read, matching the other reports.</p>
<h2 id="leaner-activity-reports" tabindex="-1"><a class="header-anchor" href="#leaner-activity-reports"><span>Leaner activity reports</span></a></h2>
<p>The activity reporting functions were streamlined and optimized further, so usage and booking popularity reports return faster and reflect every booking across the objects you can reach.</p>
<p>See the <a href="/solutions/bookable/concepts/activity">activity concept</a> and the <a href="/solutions/bookable/examples/activity-reports">activity reports example</a>.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.6.1 Gap aware availability</title>
      <link>https://developer.samna.io/solutions/bookable/changelog#v1.6.1</link>
      <guid isPermaLink="true">https://developer.samna.io/solutions/bookable/changelog#v1.6.1</guid>
      <pubDate>Tue, 23 Jun 2026 00:00:00 GMT</pubDate>
      <description>Bookings are now checked against the real open windows, so a request that crosses a closed gap is rejected with the exact blocked windows returned.</description>
      <content:encoded><![CDATA[<p>The <a href="/libraries/nimcal">NimCal calendar engine</a> behind bookings got an upgrade. Gap awareness is now in, so a booking that reaches across a closed gap is rejected, and the availability and conflict methods and their support helpers are renamed and upgraded across the board.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.6.0 Asset attachments and activity reports</title>
      <link>https://developer.samna.io/solutions/bookable/changelog#v1.6.0</link>
      <guid isPermaLink="true">https://developer.samna.io/solutions/bookable/changelog#v1.6.0</guid>
      <pubDate>Mon, 22 Jun 2026 00:00:00 GMT</pubDate>
      <description>Assets attach to any object and become readable the moment you upload them, with new activity reports for usage and booking popularity.</description>
      <content:encoded><![CDATA[<p>Assets can now be pinned to any object, and a freshly created asset is readable straight away. This release also expands the activity reports with new usage and booking popularity breakdowns.</p>
<h2 id="asset-attachments" tabindex="-1"><a class="header-anchor" href="#asset-attachments"><span>Asset attachments</span></a></h2>
<p>You can attach an asset to an object, list those attachments, reorder them, and detach them.</p>
<table>
<thead>
<tr>
<th>Endpoint</th>
<th>Effect</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>POST /api/asset/{id}/object</code></td>
<td>Attach the asset to an object</td>
</tr>
<tr>
<td><code>GET /api/asset/{id}/object</code></td>
<td>List the objects an asset is attached to</td>
</tr>
<tr>
<td><code>PATCH /api/asset/{id}/object/{object_id}</code></td>
<td>Update an attachment, such as its <code>index</code></td>
</tr>
<tr>
<td><code>DELETE /api/asset/{id}/object/{object_id}</code></td>
<td>Detach the asset from an object</td>
</tr>
</tbody>
</table>
<p>Attaching needs read access on the asset and write access on the object you attach it to. Attaching the same asset to the same object twice answers <code>409</code>. See the <a href="/solutions/bookable/concepts/asset">asset concept</a>.</p>
<h2 id="reading-your-assets" tabindex="-1"><a class="header-anchor" href="#reading-your-assets"><span>Reading your assets</span></a></h2>
<p>Creating an asset now grants you access to it straight away. Right after <code>POST /api/asset</code> you can call <code>GET /api/asset/{id}</code> for its record and <code>GET /api/asset/{id}/data</code> for the binary, and both answer <code>200</code>. You can also reach an asset through any object you already have access to: if you can read the object an asset is attached to, you can read the asset and its data.</p>
<h2 id="image-references-on-assets" tabindex="-1"><a class="header-anchor" href="#image-references-on-assets"><span>Image references on assets</span></a></h2>
<p><code>banner_id</code> and <code>icon</code> are validated when you send them.</p>
<ul>
<li><code>banner_id</code> must point to an asset you can read whose content is an image, otherwise the request answers <code>400</code>, or <code>404</code> when the referenced asset does not exist.</li>
<li><code>icon</code> accepts an icon name, a url, or the id of a readable image asset, and may not be empty.</li>
<li><code>type</code> sets the asset role: <code>profile</code>, <code>banner</code> or <code>logo</code> for images, <code>activity_template</code> or <code>notification_template</code> for templates, and null for a regular asset. The image roles require an image content type.</li>
</ul>
<h2 id="activity-reports" tabindex="-1"><a class="header-anchor" href="#activity-reports"><span>Activity reports</span></a></h2>
<p>New reports are available under <code>GET /api/activity/{func}</code>.</p>
<table>
<thead>
<tr>
<th>Report</th>
<th>What it returns</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>type_location_distribution</code></td>
<td>Bookable type usage spread across locations</td>
</tr>
<tr>
<td><code>most_used_locations_by_organization</code></td>
<td>The busiest locations per organization</td>
</tr>
<tr>
<td><code>object_creations</code>, <code>object_updates</code>, <code>object_deletions</code></td>
<td>How many records were created, updated and deleted</td>
</tr>
<tr>
<td><code>most_popular_booking</code></td>
<td>Popularity buckets with the peak bucket marked</td>
</tr>
</tbody>
</table>
<p>The <code>concurrent_bookings</code> report now accepts one or more <code>object</code> anchors written as <code>object_type:object_id</code>, each resolved down to every bookable beneath it, together with a <code>frequency</code> of <code>hour</code>, <code>day</code>, <code>week</code>, <code>month</code> or <code>year</code> and a <code>start_date</code> and <code>end_date</code> window. The <code>total_bookings_by_period</code> report fills quiet buckets with zeros so every period in the range is returned, including the empty ones.</p>
<p><strong>Breaking</strong>: the <code>most_popular_hour</code> report is no longer available. Use <code>most_popular_booking</code> for popularity buckets.</p>
<p>See the <a href="/solutions/bookable/concepts/activity">activity concept</a> and the <a href="/solutions/bookable/examples/activity-reports">activity reports example</a>.</p>
<h2 id="faster-activity-updates" tabindex="-1"><a class="header-anchor" href="#faster-activity-updates"><span>Faster activity updates</span></a></h2>
<p>Activity that depends on a booking finishing now updates promptly.</p>
<ul>
<li>A booking's <code>booking_ended</code> entry and the usage and popularity reports built on it appear right after the booking ends, instead of on a periodic catch up.</li>
<li>Adding a booking that ends sooner than the next one already due moves that update forward automatically.</li>
<li>After a restart the service restores its schedule from the next upcoming booking, so no booking end is missed or delayed.</li>
</ul>
<h2 id="pagination-with-embedded-relations" tabindex="-1"><a class="header-anchor" href="#pagination-with-embedded-relations"><span>Pagination with embedded relations</span></a></h2>
<p>Paging through a list while embedding relations with <code>with</code> now works. The cursor no longer fails when a join shares the <code>id</code> column.</p>
]]></content:encoded>
    </item>
    <item>
      <title>v1.5.4 Readable access levels</title>
      <link>https://developer.samna.io/solutions/bookable/changelog#v1.5.4</link>
      <guid isPermaLink="true">https://developer.samna.io/solutions/bookable/changelog#v1.5.4</guid>
      <pubDate>Thu, 18 Jun 2026 00:00:00 GMT</pubDate>
      <description>Claim permissions can now be sent and read back as readable level names, not only as numeric masks.</description>
      <content:encoded><![CDATA[<p>Claim permissions no longer have to be written as raw bitmask integers. You can send them as readable level names and ask any claim response to return the names alongside the integer.</p>
<h2 id="sending-access" tabindex="-1"><a class="header-anchor" href="#sending-access"><span>Sending access</span></a></h2>
<p>Send <code>access</code> in whichever shape fits your client when you create or update a claim or a claim object binding. All four forms describe the same permission set.</p>
<table>
<thead>
<tr>
<th>Form</th>
<th>Example</th>
</tr>
</thead>
<tbody>
<tr>
<td>Integer mask</td>
<td><code>6</code></td>
</tr>
<tr>
<td>Name array</td>
<td><code>[&quot;READ&quot;, &quot;WRITE&quot;]</code></td>
</tr>
<tr>
<td>Pipe joined names</td>
<td><code>&quot;READ|WRITE&quot;</code></td>
</tr>
<tr>
<td>Pipe joined integers</td>
<td><code>&quot;4|2&quot;</code></td>
</tr>
</tbody>
</table>
<p>On a claim, granting <code>WRITE</code> also grants <code>READ</code> and <code>EXECUTE</code>, so the stored permission can be wider than the literal value you send. A claim object binding keeps the exact value. The <a href="/solutions/bookable/concepts/access">access levels guide</a> lists every level name and the bit it maps to.</p>
<h2 id="reading-access-back" tabindex="-1"><a class="header-anchor" href="#reading-access-back"><span>Reading access back</span></a></h2>
<p>Responses always include <code>access</code> as an integer. Add <code>format=slice</code> for an <code>access_slice</code> array of level names, <code>format=string</code> for a pipe joined <code>access_string</code>, or pass both. Without <code>format</code>, only the integer <code>access</code> field is present.</p>
<pre data-lang="http" class="shiki github-dark" style="background-color:#24292e;color:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#F97583">GET</span><span style="color:#E1E4E8"> /api/claim/{id}?format=slice&#x26;format=string</span></span>
<span class="line"></span></code></pre>
<pre data-lang="json" class="shiki github-dark" style="background-color:#24292e;color:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#E1E4E8">{</span></span>
<span class="line"><span style="color:#79B8FF">  "data"</span><span style="color:#E1E4E8">: {</span></span>
<span class="line"><span style="color:#79B8FF">    "access"</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">14</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#79B8FF">    "access_slice"</span><span style="color:#E1E4E8">: [</span><span style="color:#9ECBFF">"EXECUTE"</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">"READ"</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">"WRITE"</span><span style="color:#E1E4E8">],</span></span>
<span class="line"><span style="color:#79B8FF">    "access_string"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"EXECUTE|READ|WRITE"</span></span>
<span class="line"><span style="color:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span>
<span class="line"></span></code></pre>
<h2 id="where-it-applies" tabindex="-1"><a class="header-anchor" href="#where-it-applies"><span>Where it applies</span></a></h2>
<p><code>format</code> works on every response that carries a claim, a claim object binding, or a user grant, including the create and delete responses and the list routes under <code>/api/claim</code>, <code>/api/me/claim</code>, and <code>/api/user/{id}/claim</code>. When you embed bindings or grants with <code>with=claim_object</code> or <code>with=user_claim</code>, the nested rows carry the readable fields too. A user grant renders the access of the claim it binds. The <a href="/solutions/bookable/examples/grant-access">grant access example</a> walks through creating a claim, binding it to a resource, and granting it to a user.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>