How it works
Robin MCP is a server that speaks the Model Context Protocol (MCP), the open standard AI assistants use to reach outside tools and data. Point your assistant at it, sign in with your Robin account, and the assistant works with Robin on your behalf. It sees and does only what your own Robin account allows.
Connect your AI assistant
Any assistant that supports remote MCP servers with OAuth sign-in can use Robin. Each guide shows where to add the server URL and how to sign in.
Claude
Team or Enterprise plan? An Owner needs to add Robin under Organization settings → Connectors first. After that, each member connects with their own Robin account.
- Open Robin in the Claude directory and connect it
- Claude sends you to Robin to sign in
- To use it, open the + menu in a chat, choose Connectors, and switch Robin on
ChatGPT
Custom connectors need developer mode, which ChatGPT offers on its Plus, Pro, Business, Enterprise, and Education plans.
- Switch on developer mode: Settings → Security and login → Developer mode
- Open ChatGPT Plugins and press + to create a connector
- Give it a name and a short description, set Connection to URL, and paste:
https://mcp.robinpowered.com/mcp- Save it. ChatGPT lists the Robin tools it found
- Sign in with your Robin account when asked
- In a new chat, pick Robin from the tools menu, or just mention Robin in your message
- ChatGPT checks with you before anything that changes data in Robin. Approve the actions you want
Gemini
Robin works with two Google products. Use the steps for the one you have.
Antigravity CLI (agy)
Antigravity CLI replaces Gemini CLI, and reads MCP servers from its own config file rather than Gemini CLI’s settings.
- You need Antigravity CLI installed
- Add Robin to
~/.gemini/config/mcp_config.json, or to a project’s.agents/mcp_config.json. If the file already lists servers, add therobinentry to itsmcpServers:
{
"mcpServers": {
"robin": {
"serverUrl": "https://mcp.robinpowered.com/mcp"
}
}
}- Start
agyand run/mcpto open the MCP manager. Robin should be listed - Sign in to Robin when Antigravity asks
Gemini Enterprise
For Google Cloud admins adding Robin as a data store in Gemini Enterprise. The Gemini app itself only takes custom MCP servers on personal Google accounts, not work accounts, so use Gemini Enterprise for your organization.
- In the Gemini Enterprise console, open Data stores, create one, and pick Custom MCP Server as its source
- Set authentication to OAuth 2.0 and fill in:
- MCP server URL
https://mcp.robinpowered.com/mcp- Authorization URL
https://auth.robinpowered.com/api/oauth/authorize- Token URL
https://auth.robinpowered.com/token- Registration URL
https://auth.robinpowered.com/register
Gemini Enterprise registers itself with Robin through this registration URL, so leave Client ID and Client secret empty.
- Name the connector, add a description and a location, and create it
- Use Actions → Reload custom actions to sign in to Robin and choose which Robin actions to turn on
- Connect the data store to the Gemini Enterprise apps or agents that should use Robin
Microsoft 365 Copilot
Coming soon. Robin is adding sign-in for Microsoft 365 Copilot, and the setup steps will appear here when it’s ready.
Microsoft Copilot Studio
- Open the agent you want to give Robin to and go to its Tools
- Choose Add a tool, then New tool, then Model Context Protocol
- Paste the server URL:
https://mcp.robinpowered.com/mcp- Under Authentication, pick OAuth 2.0 with the Dynamic discovery type
- Create a connection, or reuse one, and sign in to Robin
- Select Add to agent
Grok
Custom connectors are for Grok Business and Enterprise. A team admin sets up Robin first. After that, each member connects with their own Robin account.
- Open grok.com/connectors (some accounts call this page Plugins), select New Connector, then Custom
- Paste this URL:
https://mcp.robinpowered.com/mcp- Sign in with your Robin account when Grok asks
Perplexity
Custom remote connectors are a Perplexity Enterprise feature. An admin can add Robin for the whole organization under Enterprise settings → Permissions → Connectors permissions, or switch on Allow members to add custom connectors so people add it themselves.
- In Perplexity, go to Account settings → Connectors, select + Custom connector, then Remote
- Name it Robin and paste this as the MCP Server URL:
https://mcp.robinpowered.com/mcp- Set Authentication to OAuth and Transport to Streamable HTTP. Perplexity finds Robin’s sign-in on its own
- Check the acknowledgement box and select Add
- Select the Robin card in Connectors and sign in with your Robin account
- An admin who added Robin for the organization then shares it from Enterprise settings → Permissions
Other MCP-supporting assistants
Robin works with any assistant that can add a remote MCP server and sign in with OAuth. Names and menus differ, but the steps are the same.
- Find where your assistant adds outside tools. It may call them connectors, integrations, apps, or MCP servers
- Add a remote server. If it asks for a transport, pick Streamable HTTP. Paste this URL:
https://mcp.robinpowered.com/mcp- If it asks how to authenticate, pick OAuth
- Sign in with your Robin account when the assistant asks
What your assistant can do in Robin
Each tool below is an action an assistant can take once it's connected. Some depend on permissions in your Robin account, and every one runs as you.
Get Departments get_departments
List the department names that actually exist in the caller's organization. Call this before filtering find_people by department. Departments are free text an admin typed, and the filter is an exact match, so a plausible guess like "Design" returns nobody when the real value is "Design & Research" — and an empty result is indistinguishable from a department with no people in it. Pass a value from this list verbatim. These are the values stored on people, not a group hierarchy, so they are exactly the set find_people filters against. An empty list means nobody in the organization has a department set: say so rather than guessing a name. Names only, no counts. For the headcount of every department — "how big is each team", "which teams are largest" — call get_department_overview instead: it returns these same strings, each with its number of active people, in one call. Never size teams by calling find_people once per value from here. This is the cheap read when all you need is the valid strings.
No parameters required.
Show Stack Plan show_stack_plan
Draw a stack plan: one row per floor, every desk as a chip coloured by its group, with a legend. Use it for questions about a WHOLE building — how space divides across floors, where there is room, whether a team is split up — and to show a PROPOSED layout before anything is written to Robin. You supply the rows, so read the building first (get_location_levels for the floors, then get_draft_data per floor for who sits where, joined to find_people for departments and managers) and pass the counts you worked out. Send the same numbers you describe in prose. Do not call this on a vague request: confirm which building and which breakdown first, since reading a building is many calls.
| Parameter | Type | Description |
|---|---|---|
lens required | "department" | "manager" | "neighborhood" | What the breakdown is by. Say which one in the conversation too. |
floors required | object[] | One entry per floor, in the order they should be listed — top floor first reads best. Include floors with desks but no allocation; omit floors with no desks at all. |
Update Desk Reservation update_desk_reservation
Changes the time of an existing desk reservation in place — extend it, shorten it, or shift it — keeping the same booking rather than cancelling and rebooking. A booking that has not started keeps its id; one already in progress may be SPLIT by the service into an elapsed part and a new live booking, and the new part does not carry the original check-in (see updatedReservations). Pass only the bound(s) that change; an omitted start or end is kept as is. The new window goes through the same guard as a fresh booking: nothing entirely in the past, a slipped start bumped forward to now and reported. Works on the calling user's own, non-recurring bookings only — someone else's reservation, a recurring series, and an assigned desk are refused with the supported route instead. Performs the write immediately once the reservation and the new time are settled; returns the previous window beside the new one and a ready-made call to put it back.
| Parameter | Type | Description |
|---|---|---|
reservationId required | string | The id of the desk reservation to change, as returned by get_user_desk_reservations or book_desk. Must be the calling user's own, non-recurring booking. |
start | string | New start as a complete ISO 8601 datetime, e.g. "2026-08-14T09:00:00-04:00". OMIT to keep the current start — an omitted start is never re-sent, which is what makes extending a booking that is already running safe. A new start that has already passed (while the booking still has time left) is bumped forward to the current instant and reported back as effectiveStartTime. |
end | string | New end as a complete ISO 8601 datetime, strictly after the (new or current) start. OMIT to keep the current end. A change that would leave the whole booking in the past is refused. |
timeZone | string | IANA timezone a zone-less start/end is expressed in (e.g. America/New_York). Defaults to the reservation's own timezone, then the request's. Pass it only when the user is clearly speaking in a different zone. |
Create Building Draft create_building_draft
Start a floor-plan draft for a building. Returns the buildingDraftId that every subsequent lifecycle call (update_draft, review_draft_changes, publish_draft) requires — child floorDraftIds are never valid there. The new draft starts at version 0.
| Parameter | Type | Description |
|---|---|---|
name required | string | Human-readable name for the draft |
buildingId required | string | The building (location) id to draft changes for |
derivedFrom | string | The allocation this draft implements, from get_stack_plan or create_allocation. Records which floor split the seating work came from. The allocation must belong to this same building. |
Create Stack Plan create_stack_plan
Save a stack plan for a building along with the business-unit options it is weighing. Each option is a set of building-wide desk totals per business unit. Returns the plan id and an id per option, which create_allocation needs to propose a floor split.
| Parameter | Type | Description |
|---|---|---|
buildingId required | string | The building being replanned. A stack plan never spans buildings. |
name required | string | What the replan is called |
description | string | What the replan is for, in the planner's words |
variations required | object[] | The options this plan is weighing. Several is normal — that is what makes them comparable. |
Discard Stack Plan discard_stack_plan
Discard a stack plan and every floor-split proposal under it. There is no way to discard one proposal on its own. Scenario drafts built from a proposal are not affected. Confirm with the user first — this takes the whole plan.
| Parameter | Type | Description |
|---|---|---|
stackPlanId required | string | The plan to discard, with everything under it |
Assign People to Desks assign_people
Start assigning or seating a person or people at a desk, floor, or building. Returns the scenario-planning workflow with the request restated — call this before create_building_draft or update_draft, then follow the steps it returns.
| Parameter | Type | Description |
|---|---|---|
who required | string | Person or people to seat, e.g. "Igor Cvitas" or "the design team" |
where required | string | Building/floor/desk context, e.g. "MCP office, Floor 10" or "Floor 2, Pod 1, Desk 5" |
End or Cancel Desk Reservation end_or_cancel_desk_reservation
Removes a desk reservation: cancels it outright when it has not started yet, or ends it at this moment when it is already in progress — one tool for both, and the result says which happened. This is the undo for book_desk. A reservation that is already over is refused, and a recurring booking requires you to say whether the user meant this one occurrence or the whole series — ask them, never assume. Works on the caller's own bookings and, with delegate permission, on someone else's. Returns the removed booking's desk, window, type and visibility, plus a ready-made book_desk call to put it back.
| Parameter | Type | Description |
|---|---|---|
reservationId required | string | The id of the desk reservation to remove, as returned by get_user_desk_reservations or book_desk. For a recurring booking this is the id of ONE occurrence — the occurrence the user is talking about — even when they want the whole series removed. |
scope | "this_occurrence" | "whole_series" | For a recurring booking only: remove just the named occurrence, or the entire recurring series. REQUIRED when the reservation is part of a series — the call is refused without it rather than guessing, because the two are not undoable in one step. Ask the user which they mean and pass their answer; do not infer one from phrasing like 'cancel my Tuesday desk'. Ignored for a one-off booking. |
notifyAssignee | boolean | When removing SOMEONE ELSE'S reservation, whether Robin notifies them. Defaults to true — a person whose desk was cancelled for them should hear about it. Set false only if the user says they will tell them directly. Has no effect when removing the caller's own booking. |
Find Desks find_desks
Resolve a human desk reference like "Pod 1, Desk 2" to desk ids, or list a floor's desks: searches a building's desks by name (case-insensitive, exact matches first), optionally narrowed by pod (zone) name and level. Omit deskName (levelId then required) to list every desk on a floor — the listing is complete and includes booking types and assignee count; an open assignable desk has deskTypes ["assigned","shared"], assigneeCount 0, and isDisabled false. If a name reference matches more than one desk, ask the user to disambiguate instead of guessing. Use the returned deskId in update_draft. Reads the LIVE floor plan, so it cannot see changes made in the current draft — for a floor as the draft would leave it, use get_draft_data. Use this when you need desk ids or the assignee on a specific desk; for desk counts by booking type, one floor or the whole building, use get_building_overview rather than listing a floor and counting.
| Parameter | Type | Description |
|---|---|---|
buildingId required | string | The building (location) id to search in |
deskName | string | The desk name as an admin says it, e.g. "Desk 2". Omit to list a floor's desks instead — levelId is then required. |
podName | string | Optional pod/zone name to narrow the search, e.g. "Pod 1" |
levelId | string | integer | Optional level (floor) id filter; required when deskName is omitted. Accepts the numeric levelId returned by review_draft_changes. |
Find Scenario Planning Metric find_scenario_planning_metric
List the workplace analytics metrics available for scenario planning in this organization — one per question: commonly booked rooms by type (space_reservations_count, group by meeting__space_type), rooms consistently under capacity (undersized_meetings_count, group by meeting__space_name), desks used by an individual (desk_checkin_days_count, group by reservation__assignee_name), and desks a team reserves on a typical day (avg_desks_used_per_day: distinct desks reserved per day the group had a booking — reservations including no-shows, not check-ins, averaged over the days the group was in, not over all office days; group by reservation__assignee_department, reservation__assignee_manager_name, or reservation__level_name). Returns each metric’s id, description, allowed group-bys, allowed filters, and date-range behavior. These are the only metrics execute_scenario_planning_metric accepts; always call this first, for the building you will execute against, and use the ids and options it returns. Takes the building_id only; needs planning access to that building.
| Parameter | Type | Description |
|---|---|---|
building_id required | integer | The building to plan for (its id, as get_office_locations returns it). Required: the catalog is returned only to a planner on the building or a collaborator on one of its drafts or stack plans, and execute_scenario_planning_metric takes the same id. |
Execute Scenario Planning Metric execute_scenario_planning_metric
Execute one of the scenario planning metrics (commonly booked rooms by type, rooms consistently under capacity, desks used by an individual, desks a team reserves on a typical day) for one building and return the result (a single value, or grouped rows when group_by is given) with the applied date range, filters, and provenance. The building is a filter in the query itself, so results cover only that building and building_id is echoed on the response; do not add the building to group_by. For how many desks a team reserves on a typical day, use avg_desks_used_per_day grouped by the lens you need (department, manager, or level) and say it counts reservations including no-shows, averaged over the days the group was in. It has a different basis from desk_checkin_days_count, which counts check-in days per person; do not present one as the other. Needs planning permission on the building, or collaborator access to one of its drafts or stack plans: viewers are refused (viewer_only), others too (no_planning_access), and collaborators may group and filter by place and time only, never by anything that names a person (aggregates_only, with the allowed dimensions in valid) — relay these in plain words and do not retry. A truncated result (truncated: true) was capped within the building, like any other grouped result; say so rather than retrying. Use find_scenario_planning_metric first for the metric ids and their allowed group-bys and filters; no other metric id is accepted. Validation failures return a structured error with the valid options — correct the arguments and retry.
| Parameter | Type | Description |
|---|---|---|
metric_id required | "space_reservations_count" | "undersized_meetings_count" | "desk_checkin_days_count" | "avg_desks_used_per_day" | Metric id from find_scenario_planning_metric. Only the scenario planning metrics are accepted; any other metric id is rejected. |
building_id required | integer | The building to report on (its id, as get_office_locations returns it), the same one passed to find_scenario_planning_metric. Required: results cover this building only. |
filters | object[] | Optional filter clauses (each: dimension + exactly one of equals/in/gt/contains/missing). Only selections from the metric's allowed_filter_selections in find_scenario_planning_metric are accepted. |
group_by | string[] | Optional grouping dimensions (1-3). Only the metric's allowed_group_bys from find_scenario_planning_metric are accepted. Grouped results are capped at 50 rows. |
start_date | string | Optional ISO start date (YYYY-MM-DD). Omit both dates for the default window (previous completed 30 days). Max range 1 year. |
end_date | string | Optional ISO end date (YYYY-MM-DD). |
min_metric_value | number | Optional minimum metric value; grouped rows below it are dropped. |
Get Draft URL get_draft_url
Get the link that opens a building draft in Robin, so the user can review and edit it there. Call it when the user asks to open, see, review or share the draft — and when you have just finished building one, since reviewing in Robin is easier than another round of tool calls. Do NOT offer a link to a draft that has no changes in it yet: a draft created only to read who sits where is an empty editor, and pointing someone at it is confusing. Takes the buildingDraftId from create_building_draft; add levelId to land on a specific floor. The link is not a share — it opens for people who already have access, so use invite_users_to_draft to give someone access.
| Parameter | Type | Description |
|---|---|---|
buildingDraftId required | string | The building-draft id from create_building_draft (never a floor draft id) |
levelId | string | integer | Optional numeric level (floor) id to open the draft on. Omit to open the draft on its default floor. |
Get Stack Plan get_stack_plan
Read one stack plan in full: the business-unit variations it is weighing, every proposed floor split, and which business units each split leaves short or overprovisioned. Read-only.
| Parameter | Type | Description |
|---|---|---|
stackPlanId required | string | The stack plan id, from get_stack_plans or create_stack_plan |
Publish Draft publish_draft
PUBLISHES a building draft to the LIVE floor plan — irreversible and visible to the whole workplace. Before calling: run review_draft_changes and confirm with the user. Requires the draft's current version (from the latest update_draft) and the numeric levelIds to publish. EMAIL: publishing always sends a summary to the publisher — whoever authorized this call — and that cannot be turned off, so never tell the user no email will go out. Every OTHER email is opt-in and off by default: sendNotifications emails the people affected (assignments changed and reservations cancelled, together — they cannot be separated), and exportCSVUserIds emails a CSV of the changes to recipients the user names, which is a separate choice from notifying the affected people. Only enable either if the user asked. Publishing is ASYNCHRONOUS: a successful return means the publish was ACCEPTED, not that the changes are live. Confirm with get_draft_status, polled three times — 10 seconds after this call, again at 20, and a last time at 30 — and never retry this tool or create another draft while a publish is in flight.
| Parameter | Type | Description |
|---|---|---|
buildingDraftId required | string | The parent building-draft id from create_building_draft (never a floor draft id) |
version required | integer | The draft's CURRENT version — must be the version returned by the latest update_draft (or 0 for a never-updated draft). Never guess; a stale version is rejected. |
levelIds required | integer | string[] | Numeric level (floor) ids of the building to publish — NOT floorDraftIds. review_draft_changes returns each floor's levelId. |
sendNotifications | boolean | Email the people this publish affects: those whose desk assignments changed AND those whose reservations get cancelled. One switch covers both groups — they cannot be notified separately. Off unless the user explicitly asked for it. This is separate from the summary the publisher always receives. |
notifyAffectedAssignees | boolean | Granular form of sendNotifications for people whose assignments changed. Behaves identically — setting either notifies both groups — so prefer sendNotifications and leave this unset. |
affectedAssigneesMessage | string | Optional note added to the notification email, in markdown. Only the assignment email carries a message; there is no equivalent for people whose reservations were cancelled. Pass only text the user gave you. |
notifyAffectedReservees | boolean | Granular form of sendNotifications for people whose reservations get cancelled. Behaves identically — setting either notifies both groups — so prefer sendNotifications and leave this unset. |
exportCSVUserIds | string[] | User ids to EMAIL a CSV of the published changes to. A separate opt-in from the notifications above, with its own recipients: these are people the user nominates to receive a record of the move — typically facilities or ops colleagues, NOT the people being seated. Omit to send no CSV. Resolve names to ids with find_people, or search_entities where the client exposes it; never guess an id. |
exportCSVMessage | string | Optional note added to the CSV email, in markdown. Ignored when exportCSVUserIds is omitted. Pass only text the user gave you. |
Invite Users to Draft invite_users_to_draft
Invite people to a building draft as collaborators or viewers, so they can review or help build a plan before it is published. This NOTIFIES the people invited, so treat it the way you would sending mail on the user's behalf: invite only who the user named, and only when they asked for it. Requires an explicit accessLevel — 'collaborator' can change the draft, 'viewer' can only read it, and NEITHER can publish it: that stays with the draft's owner. Takes NO version: sharing is outside the draft's version chain, so there is nothing to chain here. Call get_draft_collaborators first to see who already has access — re-inviting someone emails them a second time for nothing.
| Parameter | Type | Description |
|---|---|---|
buildingDraftId required | string | What to share. The parent building-draft id from create_building_draft, or a stack plan id from get_stack_plans or create_stack_plan — sharing a plan carries the floor splits under it. Never a floor draft id, and never an allocation id: share the plan those belong to instead. |
userIds required | string[] | Robin user ids of the people to invite. Resolve names or emails to ids first — with find_people, or search_entities where the client exposes it. Never guess an id: an invite is an email to a real person, so a wrong id is a message to the wrong colleague. |
accessLevel required | "collaborator" | "viewer" | What the invited people may do. "collaborator" can CHANGE the draft — assignments, desk types, neighborhoods — and is what someone helping build the plan needs. "viewer" can only read it and changes nothing. NEITHER can publish: publishing stays with the draft's owner, so inviting someone never hands off the decision to go live, and there is no level here that would. Ask the user which of the two they mean rather than assuming; a request to "share a draft for review" is a viewer unless they say otherwise. |
Search Entities search_entities
Find people, desks, spaces, or bookable resources in the caller's organization by name or email. Returns matching entities with their IDs, types, and location context, for use as inputs to other tools. Optionally restrict to specific entity types; omit entityTypes to search everything.
| Parameter | Type | Description |
|---|---|---|
query required | string | |
entityTypes | "CUSTOM_RESOURCE" | "DESK" | "SPACE" | "USER"[] | null | |
limit | integer | null |
Get Neighborhood Colors get_neighborhood_colors
List the neighborhood colour slots this organization actually has, with the hex each one renders as. Call this before sending a neighborhood to update_draft, and pass an `id` from it verbatim. `id` is what update_draft's `color` field takes — a named slot such as `NH_A`, never a CSS colour. `base` and `dark.base` are only what that slot looks like on the floor plan, so a choice can be described to the user ("the teal one"); never send `base` or `dark.base` to update_draft, which fills the display hex in itself. A caller only DESCRIBING a colour reads `base` and can ignore everything else. A caller DRAWING a neighborhood needs the set matching the background it draws on: `base` + `label` on a light ground, `dark.base` + `dark.label` on a dark one. `base` is the fill; `label` is the pill the neighborhood's NAME sits in, as background plus text colour. Worth knowing which half actually changes: the fill is the same in both themes for every slot, and the label pill inverts. So a renderer that only fills shapes can read `base` alone and be correct on any ground; one that writes names has to switch. Worth the round trip, because the vocabulary is not guessable in the direction that costs something. The schema declares fourteen slot names (`NH_A`..`NH_N`) but only the ones returned here have a colour defined, and a neighborhood written with any other name is accepted by every validator upstream, publishes, and then renders with no colour at all. update_draft refuses a slot missing from this list rather than landing that, so a name invented from the pattern costs the same round trip and an error. The list is org-wide and identical on every floor, so one call covers a whole session. Which slots are FREE is per-floor and is not answered here: read the floor with get_draft_data and pick one no neighborhood there is already using, so two neighborhoods on one plan are not drawn the same colour.
No parameters required.
Get Draft Collaborators get_draft_collaborators
Who a building draft or stack plan has been shared with, and at what level. Returns each person it was shared with through invite_users_to_draft, with their user id, name, email, and access level (`collaborator` or `viewer`). It does NOT list the owner or the building's planners unless someone invited them: they have access through their planning permission, not through a share, so an empty list means "shared with nobody", not "nobody can see it". find_people_for_draft reports each person's access to the draft, including those permission-based `editor`s. Call this BEFORE invite_users_to_draft. Inviting emails the people named, and nothing here checks whether they already have access, so this is what keeps a share from landing in the inbox of someone who has had the draft open all along. `editor` is the only level that can PUBLISH the draft, and it comes from planning permission. A `collaborator` can change everything in it but cannot publish; a `viewer` can only read. invite_users_to_draft grants `collaborator` or `viewer` only, never `editor`. Takes the buildingDraftId from create_building_draft, and no version: sharing sits outside the draft's version chain.
| Parameter | Type | Description |
|---|---|---|
buildingDraftId required | string |
Find Reports find_reports
Everyone who reports to one manager, directly or through other managers — the manager's whole organization in one call, each person with their depth (depth 1 is a direct report, 2 a report's report, and so on). This is the skip-level question find_people cannot answer: its managerIds filter returns DIRECT reports only. Use find_reports when the request is a leader's whole org ("everyone under Jack", "seat Priya's organization on Floor 1"); use find_people with managerIds when it is one manager's own team. managerId is a user id, never a name — get_managers lists the people who manage someone, with the ids this takes; two managers with the same name is a question for the user, not a coin flip. The manager themselves is not included in the result. The walk stops at a deactivated person: they are not returned, and it does not continue through them to their own reports, because a manager line still pointing at someone who has left is stale. So a team whose manager has left does NOT reappear under the manager above — it is absent, and a gap in the org chart is a gap, not a reparented team. A deactivated manager has an empty chain. Every intermediate manager is itself a member, so each person's managerId can be read off the same result — group by it to see the org's shape. The one exception is the queried manager, who is not in the result: their depth-1 reports carry a managerId with no member to label it, and that label comes from the get_managers lookup you started with. `name` is the display name and can be null — fall back to givenName and familyName, which are selected for exactly that reason. maxDepth bounds the walk. It takes 1 to 20 and defaults to its maximum of 20, which is deeper than any real org chart, so the default returns the whole chain and nothing is clipped. A maxDepth you choose yourself returns a PARTIAL chain, and nothing in the result says so — there is no truncation flag, and total counts the people returned, not the people below. So when you pass a maxDepth, say the answer is bounded to that depth and name it; members at the deepest level may have reports that were never fetched. Never present a bounded chain as a whole org. total is the number of people returned. The whole chain comes back in one response; there is no paging. Above 1000 people the call FAILS instead of returning part of the chain — ask about a manager further down, or pass a lower maxDepth and then state the bound, as above.
| Parameter | Type | Description |
|---|---|---|
managerId required | string | |
maxDepth | integer | null |
Get Current User Info get_current_user_info
Get the calling user's identity, workplace context, and the current time in one call: Robin user id, name, email, timezone, default building (id and name), current organization, and currentDateTime — the authoritative current instant. Takes no input. Call this FIRST for any request about the calling user themselves ("my desk", "my building", "book me a desk"): it replaces search_entities for self-referential requests, and its userId, timezone, and defaultLocationId are the values to pass to any tool that takes a user id, a timezone, or a building (location) id — defaultLocationId is the building the floor and desk lookups (get_location_levels, find_desks) take, and defaultLevelId is the floor; use them unless the user names another. Call it FIRST as well for any request carrying a relative date ("today", "tomorrow", "next Tuesday", "this week"): resolve that date against currentDateTime in the user's timezone rather than assuming what today is, because Robin's reservation service accepts a booking in the past silently.
No parameters required.
Check Desk Availability check_desk_availability
Pre-flight a desk booking: for up to 100 desks and up to 30 time windows (at most 750 desk-window pairs per call), report each desk's availability status per window, the typed reasons it cannot be booked (policy violations, access, capacity — each carrying the rule's value and a plain-language explanation, so a rejection contains the rule it broke), the reservations occupying it, and any exclusion windows (released assigned desks — bookable as reverse_hotel when the window fits entirely inside one). For a multi-day or recurring plan, pass each day's span as its own window in ONE call instead of calling once per day. This answers whether SPECIFIC desks can be booked; it is not a floor sweep — narrow the desks first (find_desks by floor or zone) and bring their ids here. Availability is evaluated for the calling user unless userId is given, and each desk reports bookedByRequestingUser — the flag that separates a desk someone ELSE holds from one the evaluated user already holds themselves (private bookings included, also when checking on someone else's behalf), the latter being reschedulable in place even though the desk reads as not bookable. Assigned desks (types ["assigned","shared"]) are judged the way the office map judges them: bookable only as reverse_hotel on a day the assignee has released the desk — releasedForWindow, assignees, and bookAs report exactly that, and bookable already folds it in, so an unreleased assigned desk nobody sits at reads status AVAILABLE but bookable false. Zone-less window bounds are read in timeZone when given (pass the same value to book_desk), else the caller's timezone. ALWAYS check the chosen desk here before book_desk: booking rejections that this surfaces in advance include conflicts, advance-booking limits, max length, working hours, and floor capacity — and capacity is only enforced here, not at booking time.
| Parameter | Type | Description |
|---|---|---|
deskIds required | string | integer[] | Desk ids to check (1-100), as returned by find_desks or search_entities. deskIds × windows may not exceed 750 per call. |
windows required | object[] | Time windows to evaluate (1-30), each an exact start/end span. For a multi-day or recurring plan, pass each day's span as its own window in one call instead of calling once per day — results come back per window, in request order. deskIds × windows may not exceed 750 per call; beyond that, split by dates or narrow the desks. |
userId | string | integer | The numeric Robin user id the availability is evaluated for (permissions, access, own-conflict checks), as returned by search_entities or get_current_user_info. Defaults to the calling user. |
timeZone | string | IANA timezone that zone-less window bounds are expressed in (e.g. America/New_York). Defaults to the caller's timezone; pass it explicitly when checking in a different building's zone — and pass the SAME value to book_desk afterwards, so both tools resolve the same absolute window. Ignored for bounds that carry their own offset or Z. |
Get Office Locations get_office_locations
List every office location (building) in the caller's organization, with address and timezone. The id of each is the buildingId the building tools take. For a building's size and shape — floors, desk counts by booking type, neighborhoods — pass it to get_building_overview. When only the floor ids and names are needed, get_location_levels is the one cheaper call.
No parameters required.
Get Department Overview get_department_overview
Headcount per department for the caller's organization in one call: every department string with the number of active people in it, plus how many people have no department at all. Reads the live directory and counts — no draft, and no call per department, so this replaces get_departments followed by find_people once per team when what you need is the size of each team. The strings are exactly what find_people's departments filter takes. Pass departments to size only the teams in scope. Department is free text on each person, not an org hierarchy: two spellings are two departments, and nothing here rolls them up.
| Parameter | Type | Description |
|---|---|---|
departments | string[] | Count only these departments — the stored strings from get_departments (matching is case-insensitive, as the directory filter is). Omit to count every department in the organization. |
Book Desk book_desk
Books a desk for a settled desk and time window, and returns everything needed to report the booking back and offer an undo. Books for the current user by default, or for another person when forUser (their email) is given — which requires delegate permission. This tool performs the write immediately; confirm the desk, window, and reservation type first (check_desk_availability shows what is bookable and which reservationType to send as bookAs). It does not re-check availability itself, but the window goes through the same guard as the read tools: start and end may carry an offset or be zone-less (then read in timeZone, the request timezone, or the caller's profile timezone, in that order), a window that has already ended is refused, and a start that has merely passed is clamped to now and reported as effectiveStartTime. Assigned desks follow their own rule: one is bookable only as reverse_hotel, and only on a day its assignee has released it — with no release (or no assignee to release it) it is seated by an admin assignment instead and cannot be booked by anyone.
| Parameter | Type | Description |
|---|---|---|
deskId required | string | The id of the desk (seat) to book, as returned by find_desks or check_desk_availability. A numeric id. |
start required | string | Reservation start as an ISO 8601 datetime, with an offset ("2026-09-10T09:00:00-04:00") or without ("2026-09-10T09:00:00"). A zone-less value is read as wall-clock time in timeZone when given, else the request timezone, else the calling user's profile timezone — the same chain check_desk_availability uses, so the window pre-flighted there is the window booked here. Bare dates and natural-language times are rejected. Resolve "today"/"tomorrow" against get_current_user_info's currentDateTime; a start that has already passed is clamped forward to now and reported as effectiveStartTime, and a window that has already ended is refused. |
end required | string | Reservation end as an ISO 8601 datetime, strictly after start, in the same form as start. |
reservationType required | "hot" | "hoteled" | "reverse_hotel" | The reservation type for this desk: 'hot' (short-term, same-day), 'hoteled' (booked for a defined period), or 'reverse_hotel' (an assigned desk on a day its assignee has released it). Use exactly what check_desk_availability reports as bookAs for the desk and window. An assigned desk that is not released has no type here — check_desk_availability reports it as bookAs null with the reason. |
visibility | "EVERYONE" | "JUST_ME" | Who can see this reservation on the floor plan. Defaults to EVERYONE; use JUST_ME only if the user asked to keep it private. |
forUser | string | Email of the person to book for, when booking on someone else's behalf. Omit to book for the current user. Resolve a name to an email with find_people; booking for others requires delegate permission. |
timeZone | string | IANA timezone the start/end are expressed in (e.g. America/New_York), and the zone the reservation is recorded in. Defaults to the request timezone, then the calling user's profile timezone; pass it explicitly when booking in a different building's zone — and pass the SAME value you gave check_desk_availability. Only when start and end both carry their own offset does it fall back to UTC. |
Stack Planning Workflow stack_planning_workflow
Read this FIRST, before loading a building's floors or calling scenario_planning_workflow, whenever the request involves: Use for viewing, evaluating, mocking up or drafting a stack plan — how business units are seated across the floors of a building. Triggers on "how are my teams using the building", "show me where teams are sitting", "can I reduce my footprint", "what if Sales moved floors", "can we absorb growth", "consolidate teams onto one floor", "create neighborhoods for each department", "build a scenario planning draft", "save this as a plan", "what plans do we have for this building", "open the Q3 plan", and on follow-ups like sharing a plan, inviting collaborators, or asking where one team sits. Consult it before rendering any stack plan or changing anything in Robin. Returns how to read the current seating, which questions this data can and cannot answer (assignments yes, desk usage no), how to identify what a business unit actually is, and when a proposal is ready to become a draft.
No parameters required.
Add Stack Plan Variation add_stack_plan_variation
Record one more set of building-wide desk totals on an existing stack plan. Use this when the numbers change: a variation freezes once an allocation implements it and cannot be edited, so a revision is a new variation on the same plan — not an edit, and not a second plan. Returns the variation id create_allocation needs.
| Parameter | Type | Description |
|---|---|---|
stackPlanId required | string | The plan to record this variation on, from get_stack_plans |
name required | string | What this variation is called, e.g. "Today's headcount + 10 Engineering" |
description | string | Why it is on the table, in the planner's words |
businessUnits required | object[] | What each business unit needs, building-wide. The whole set, not a delta from another variation. |
Scenario Planning Workflow scenario_planning_workflow
Read this FIRST, before calling create_building_draft, update_draft, or publish_draft, whenever the request involves: Assign or move people to desks ("Assign Sam to Floor 2", "Move the team to Pod 1"), change desk types or reservability, edit floor plans, and publish draft changes via the Robin MCP tools. Returns which tool to use in which order, the draft lifecycle rules, and the conventions for talking to the user while planning.
No parameters required.
Update Draft update_draft
Apply changes to a building draft under an optimistic lock: per-desk changes (reservability, reservation types, assignments) and per-neighborhood changes (name, colour slot, desk membership, creation and removal). Takes the buildingDraftId and the draft's current version; returns the bumped version, which the next update_draft or publish_draft call must use. Two reads come first for a neighborhood change: get_neighborhood_colors, because a colour is a named slot from that list and not a hex, and get_draft_data, because a neighborhood's desk membership REPLACES what is there. Neighborhood changes on several floors are grouped by the tool; the caller sends one batch.
| Parameter | Type | Description |
|---|---|---|
buildingDraftId required | string | The parent building-draft id from create_building_draft (never a floor draft id) |
version required | integer | The draft's CURRENT version: 0 for a fresh draft, otherwise the version returned by the previous update_draft. If the current version is unknown, re-fetch it via get_draft_version. Guessing (e.g. 1) fails on fresh drafts. |
changes required | object | object[] | The changes to upsert into the draft: desk changes (the default kind) and neighborhood changes, in any mix. Neighborhood changes may span floors — the tool groups them. |
Review Draft Changes review_draft_changes
Review what a building draft would change before publishing: per-floor summary with each floor's numeric levelId (the ids publish_draft takes), change flags, desk-statistics deltas, per-neighborhood before/after deltas, the existing bookings publishing would cancel, and the draft status. Always review before publish_draft, and always show the user any reservationsToCancel — publishing cancels those people's bookings and cannot be undone. Set detailed: true for the full payload with assignment schedules.
| Parameter | Type | Description |
|---|---|---|
buildingDraftId required | string | The parent building-draft id from create_building_draft (never a floor draft id) |
detailed | boolean | false (default): decision-sized per-floor summary. true: the API's full payload including assignment schedules. |
Get Draft Version get_draft_version
Fetch the current optimistic-lock version of a building draft. Support tool for recovery: versions normally come from create_building_draft (0) and each update_draft result — use this only when the current version is unknown (lost context) or an update failed with a stale-version error. Takes the buildingDraftId returned by create_building_draft.
| Parameter | Type | Description |
|---|---|---|
buildingDraftId required | string |
Find Available Desks find_available_desks
Answer "what desks are free on floor X for this window" in one call: sweeps an entire level for a time window and returns the desks that are bookable by the calling user (with names, booking types, pod, amenities, and the reservationType to book them as), plus how many are busy or off-limits. Assigned desks follow the office map's rule: one is listed only when its assignee has released it for the window (then with bookAs reverse_hotel) — the rest are counted in assignedNotBookableCount. Optionally filter to desks with specific amenities — call once without deskAmenityIds to get the floor's amenity ids from levelDeskAmenities, then re-call with them. Use this instead of paging find_desks results through check_desk_availability in batches. A window that has already ended is refused; a start that has merely passed is clamped to now and reported as effectiveStartTime, which then replaces the requested start everywhere. Availability reflects the caller's own access and conflicts; still call check_desk_availability on the desk you pick before book_desk, since policy limits and floor capacity are only reported there.
| Parameter | Type | Description |
|---|---|---|
levelId required | string | integer | The level (floor) id to sweep, as returned by get_location_levels or find_desks |
start required | string | Window start as an ISO 8601 datetime, e.g. "2026-08-27T09:00:00-04:00". Bare dates and natural-language times are rejected; a value without an offset or Z is read in the request timezone, then the calling user's profile timezone. Resolve "today"/"tomorrow" against get_current_user_info's currentDateTime rather than an assumed date — a start that has already passed is clamped forward to the current instant and returned as effectiveStartTime. |
end required | string | Window end as an ISO 8601 datetime, strictly after start. A window that has already ended fails the call rather than being answered — nothing can be booked into it. |
deskAmenityIds | string | integer[] | Optional amenity ids every returned desk must have, e.g. a monitor or a standing desk. Ids come from levelDeskAmenities in this tool's own result — call once without the filter to learn the floor's amenity vocabulary, then re-call with the ids. Multiple ids narrow further (a desk must satisfy the filter to count as available). Never guess an id from a name like "monitor". |
Get User Desk Reservations get_user_desk_reservations
List a user's desk reservations within a time range: which desk (with its floor and building names), when, the reservation type, check-in state, and who booked it, sorted earliest-first. Omit userId for the calling user (it resolves automatically); resolve anyone else with search_entities. Times are ISO 8601 datetimes; use the start and end of the user's day (in their timezone) to answer questions about a specific day. For preference questions ("my usual desk", "which days am I normally in", "how often do I come in"), query a past range (4-8 weeks works well) and read the server-computed `analytics` block instead of counting reservations yourself.
| Parameter | Type | Description |
|---|---|---|
userId | string | integer | The Robin user id to list desk reservations for, as returned by search_entities. OMIT for the calling user — it resolves automatically. |
startTime required | string | Range start as an ISO 8601 datetime, e.g. "2026-08-13T00:00:00Z" |
endTime required | string | Range end as an ISO 8601 datetime, after startTime |
Get Building Hours get_building_hours
The building's working day, date by date: opening time frames for a range of calendar dates, with closed days (weekends, holidays, closures) coming back as empty. Use it to propose real booking hours instead of assuming 9:00–17:00, and to skip closed days when planning multi-day bookings — a day with open=false should never be booked. locationId defaults to the calling user's default building; all times are local wall-clock times in the building's own timezone (returned alongside).
| Parameter | Type | Description |
|---|---|---|
locationId | string | integer | The building (location) id, as returned by get_office_locations. OMIT for the calling user's default building — it resolves automatically. |
startDate required | string | First calendar date of the range, "YYYY-MM-DD" — a date on the building's own calendar, not an instant, so no time or offset. |
endDate required | string | Last calendar date of the range, inclusive — same day as startDate for a single day, at most 31 days after it. |
Get Building Overview get_building_overview
Read a building as floors × desk types in one call: every floor with its desk counts (total, active, disabled) and how many active desks are of each booking type, plus each floor's neighborhoods when includeNeighborhoods is set. Reads the LIVE floor plan and needs no draft. This is the opening read for a stack plan and the cheap way to tell an assignment-desk building from a hotel-desk one; it does not say who sits where — that is get_draft_data (draft) or find_desks (live, one floor).
| Parameter | Type | Description |
|---|---|---|
buildingId required | string | The building (location) id from get_office_locations |
levelIds | string | integer[] | Read only these floors, by level id. Omit both filters to read the whole building. Combine with levelNames to add floors named rather than identified. |
levelNames | string[] | Read only these floors, by name as the user says it ("Floor 1"). Case-insensitive exact match against the building's floor names; an unknown name is an error that lists the floors that exist. |
includeNeighborhoods | boolean | Also return each floor's neighborhoods (name, colour slot, desk membership and type counts). Off by default — turn it on when the question involves neighborhoods or the building is mostly hotel desks, where the plan is areas rather than seats. |
Find People find_people
Find people in the caller's organization and return the user ids the draft tools take. Built for a request that names a group — "the design team", "everyone reporting to Ana", "people who joined this month" — but userNameFilter also resolves a single named person, and in a draft-only toolset this is the tool for that. Where search_entities is also available it matches fuzzily and scores results, so prefer it for a half-remembered name. Each filter narrows the result; combining them ANDs them together. - departments: EXACT match against free text an admin typed. Call get_departments first and pass one or more values from it verbatim. A guessed string returns zero people, which is indistinguishable from a team that has nobody in it, so never guess. - managerIds: user ids, not names. Call get_managers for the real set and pass an id from it; that is also what makes an ambiguous "Ana" visible rather than silently resolved. Failing that, this tool's userNameFilter or search_entities can look the manager up. Returns DIRECT reports only — for everyone under a leader at every depth, use find_reports. - userNameFilter: a PREFIX match on name, surname, slug, or email — "ana" finds "Ana Kovač", "kova" finds her by surname, but a fragment from the middle of a name finds nothing — ask for a cleaner spelling rather than guessing, or use search_entities where the client exposes it. - addedWithinDays: people added to the organization in the last N days, for seating a joiner cohort. - statuses: omit it and deactivated people are excluded, which is what seating wants. Pass DEACTIVATED to explain why someone expected is missing. Each person comes back with department, title, and their DIRECT manager (managerId plus the manager's name), which is what lets results be grouped by team, seniority, or reporting line without a second lookup. It is one level only — a manager's manager is not resolved, so a senior leader's wider org cannot be assembled from this call; find_reports walks the whole chain. accountUsersTotal is the total number of matches, not the size of this page: state it to the user before changing any desks, so seating a 40-person department is a decision rather than a surprise. hasNextPage and nextPageOffset page through the rest. Use this when you need the people themselves — their ids, managers, titles. When the question is only how big each team is, get_department_overview returns every department's headcount in one call; do not size teams by calling this once per department. Omitting every filter lists the whole directory one page at a time. That is occasionally what someone wants, but it is rarely the answer to a seating request — pass at least one filter unless a full listing was asked for.
| Parameter | Type | Description |
|---|---|---|
departments | string[] | null | |
managerIds | string[] | null | |
userNameFilter | string | null | |
statuses | "ACTIVE" | "ADDED" | "DEACTIVATED" | "INVITE_EXPIRED" | "INVITE_PENDING"[] | null | |
addedWithinDays | integer | null | |
offset | integer | |
limit | integer |
Get Draft Data get_draft_data
Read one floor's editable draft state: its desks (booking types, who the draft seats on each) and its neighborhoods (name, color slot, desk membership), each flagged for whether it differs from the live plan. This is the draft-aware counterpart to find_desks, which reads the LIVE plan and cannot see changes made in the current draft. Also returns the draft's current version, so you do not need get_draft_version. levelId is REQUIRED — each call reads exactly one floor. It needs a draft: for neighborhoods or desk counts across floors with no draft in play, get_building_overview with includeNeighborhoods reads the live plan for the whole building in one call and creates nothing. NOTE: this is not a pure read. It creates the floor's child draft if one does not exist yet (which is what you want right after create_building_draft) and syncs newly-added seats onto it; repeating the call changes nothing further.
| Parameter | Type | Description |
|---|---|---|
buildingDraftId required | string | The parent building-draft id from create_building_draft (never a floor draft id) |
levelId required | string | integer | The level (floor) id to read. Required — omitting it would read some other floor. Accepts the numeric levelId returned by review_draft_changes. |
Get Draft Assignments get_draft_assignments
Where specific people sit according to a draft: their desk assignments with the draft's pending changes already merged over the live floor plan, for one or more people looked up by userId or email. Use it before moving someone, to see which desks they currently hold (and would be released), and to confirm an assignment landed. find_desks reads the LIVE plan only and cannot see assignments made in the current draft; this can.
| Parameter | Type | Description |
|---|---|---|
buildingDraftId required | string | |
assignees required | object[] |
Get Stack Plans get_stack_plans
List the saved stack plans for a building — named replans, each holding the business-unit variations it is weighing. Returns names and descriptions only; call get_stack_plan for one plan's variations, floor splits and shortfalls. Read-only. Building drafts are not listed here or by any other tool, so do not offer to look one up; a draft can only be reached by the id you were given.
| Parameter | Type | Description |
|---|---|---|
buildingId required | string | The building whose stack plans to list. A stack plan never spans buildings. |
Add Users to Draft Queue add_users_to_draft_queue
Park people on one floor's assignment queue in a draft — the holding list for people you mean to seat but cannot yet. The main use: a group does not fit, so you seat everyone who fits and queue the overflow, then the admin adds desks and you assign the queued people. Queues are PER FLOOR, so levelId is required. Takes NO version — queue changes are outside the draft's version optimistic lock, so there is nothing to chain here. Read the queue back with get_draft_queue.
| Parameter | Type | Description |
|---|---|---|
buildingDraftId required | string | The parent building-draft id from create_building_draft (never a floor draft id) |
levelId required | string | integer | The level (floor) id whose queue to change — each floor has its own queue. Accepts the numeric levelId returned by review_draft_changes. |
userIds required | string[] | Robin user ids. Resolve names or emails to ids first — with find_people, or search_entities where the client exposes it. Never guess an id. |
Get Draft Queue get_draft_queue
Who is waiting for a desk on one floor of a draft: the people on that floor's assignment queue, with names, emails, and departments. A queue holds people an admin means to seat but cannot yet — typically because a group did not fit, so the overflow was parked here until desks are added. Queues are PER FLOOR, so levelId is required. Use this before assigning from the queue, and after, to confirm it is clear. Note that assigning someone does NOT remove them from the queue — remove_users_from_draft_queue does that.
| Parameter | Type | Description |
|---|---|---|
draftId required | string | |
levelId required | string | |
offset | integer | |
limit | integer |
Get Location Levels get_location_levels
Get an office location (building) by ID with its address and timezone, plus every floor with that floor's total desk count. Use it to resolve a floor name to the numeric levelId the draft tools take. deskCount includes inactive desks — to see individual desks, their booking types, and who sits on them, use find_desks with that levelId. For desk counts by booking type across floors, or each floor's neighborhoods, use get_building_overview instead.
| Parameter | Type | Description |
|---|---|---|
locationId required | string |
Get Managers get_managers
List the people who actually manage someone in the caller's organization, with the user ids the manager filters take. Call this before filtering find_people or find_people_for_draft by `managerIds`. Those filters take ids, never names, so without this list the only way to reach "everyone reporting to Ana" is to look Ana up by name first and hope the right Ana came back. This returns the actual set of managers, so a request naming a reporting line can be resolved against it — and an ambiguous one ("Ana") is visibly ambiguous rather than silently resolved to whoever matched first. This is the manager counterpart of get_departments, and it has the same meaning: these are people referenced as somebody's manager on their user record, not an org-chart hierarchy. It is exactly the set the filters compare against. Someone who manages nobody does not appear, however senior they are. One level, like the filters it serves: filtering by a manager returns their DIRECT reports, not their reports' reports. For a senior leader's whole organization — reports of reports, all the way down — pass the id from here to find_reports instead. `name` is the display name and can be null — fall back to givenName and familyName. Department and title are included to tell two people with the same name apart; ask the user which they meant rather than picking one. An empty list means nobody in the organization has a manager set: say so rather than guessing a name.
No parameters required.
Get Draft Status get_draft_status
Current status of ONE building draft — how you confirm a publish actually landed. publish_draft returns as soon as the publish is ACCEPTED, not when it is live, because publishing is asynchronous; this tool is how you learn what became of it. Takes the buildingDraftId from create_building_draft. After publish_draft, poll this THREE times: 10 seconds after the publish, again at 20 seconds, and a last time at 30 seconds. Stop at the first settled status. - published — the changes are live. Confirm to the user and stop. - publishing — still in flight. Poll again if you have polls left. Never retry publish_draft and never create a new draft while one is publishing. - publishing_failed — the publish did not go through. Report it and stop. - discarded — the draft was thrown away; nothing was published. - open — the publish never started. Still publishing after the third poll? Stop polling and tell the user their changes are on the way and to check back in a few minutes; a large set of changes takes longer to go live. Do not retry the publish and do not create another draft. Never narrate any of this. The user does not need to hear about polling, publish states, or asynchronous anything — only whether their changes are live yet. This reads one draft whose id you already hold. It cannot list or search drafts, and it returns nothing but that draft's id and status.
| Parameter | Type | Description |
|---|---|---|
buildingDraftId required | string |
Remove Users from Draft Queue remove_users_from_draft_queue
Clear people from one floor's assignment queue in a draft. Call this AFTER you seat a queued person: assigning someone a desk with update_draft does not remove them from the queue, so the queue keeps reporting them as waiting until you remove them here. Also use it when the user changes their mind about who is waiting. Queues are PER FLOOR, so levelId is required. Takes NO version — queue changes are outside the draft's version optimistic lock.
| Parameter | Type | Description |
|---|---|---|
buildingDraftId required | string | The parent building-draft id from create_building_draft (never a floor draft id) |
levelId required | string | integer | The level (floor) id whose queue to change — each floor has its own queue. Accepts the numeric levelId returned by review_draft_changes. |
userIds required | string[] | Robin user ids. Resolve names or emails to ids first — with find_people, or search_entities where the client exposes it. Never guess an id. |
Find People for a Draft find_people_for_draft
Search the WHOLE organization's directory, and see each person's relationship to one building draft: what access they have to it (if any), and whether the draft seats them. It searches everyone, not the draft's members — most results will have no connection to the draft at all, which is the point when you are looking for someone to invite. Use it BEFORE invite_users_to_draft to check whether the people you are about to email already have access. With assignmentStatus UNASSIGNED it flips to the other question: who has no desk in this plan. This tool does NOT invite anyone — invite_users_to_draft does that. For a directory lookup with no draft in play, use find_people; to list the people already on a draft, use get_draft_collaborators.
| Parameter | Type | Description |
|---|---|---|
buildingDraftId required | string | The parent building-draft id from create_building_draft (never a floor draft id). This scopes the ANSWERS, not the search: people are found across the whole organization either way, and each one is reported with their access to, and assignment in, this draft. |
userNameFilter | string | A PREFIX match on name, surname, slug, or email: "ana" finds Ana Kovač, "kova" finds her by surname, a fragment from the middle of a name finds nothing. |
departments | string[] | EXACT match against free text an admin typed. Call get_departments first and pass values from it verbatim — a guessed string returns zero people, which is indistinguishable from an empty team. |
managerIds | string[] | User ids, not names — call get_managers for the real set and pass an id from it. Returns that manager's DIRECT reports only; find_reports walks a leader's whole chain. |
assignmentStatus | "ASSIGNED" | "UNASSIGNED" | Filter by whether the person has a desk IN THIS DRAFT, with the draft's pending changes applied. "UNASSIGNED" is the one that answers "who still needs a seat in this plan?" — combine it with departments to ask that of one team. Omit to filter on nothing draft-related, which is the right choice when you only want access levels. |
offset | integer | |
limit | integer |
Create Allocation create_allocation
Propose a way of placing one of a stack plan's options across the building's floors. Several proposals can implement the same option, which is how layouts get compared. Returns the proposal's version for update_allocation, which business units the split leaves short or overprovisioned, and each floor in the starting split against its real desk count (floorCapacity); an unfinished split is saved rather than refused.
| Parameter | Type | Description |
|---|---|---|
stackPlanId required | string | The plan this split belongs to |
allocationId required | string | Which of the plan's options this implements — a variation id from get_stack_plan, create_stack_plan or add_stack_plan_variation |
name required | string | What this layout is called, e.g. "Consolidated" |
description | string | |
floors | object[] | The starting split. May be left out and filled in later with update_allocation. |
Update Allocation update_allocation
Rewrite the floor split of a proposal. Each floor sent replaces that floor outright; floors left out are untouched, and a floor sent with no business units is cleared. Returns the new version, what the split still leaves short against its option, and each written floor against its real desk count (floorCapacity) — a split can add up and still not fit the floor. Requires the version last read.
| Parameter | Type | Description |
|---|---|---|
allocationId required | string | The proposal to change |
version required | integer | The version last read, from get_stack_plan or a previous update_allocation. A stale value is refused. |
floors required | object[] | Floors to write. Each replaces that floor outright; floors left out are untouched. Send a floor with no business units to clear it. Send only the floors the user asked to change, including after a stale-version retry: re-sending a floor you re-read writes it again for nothing. |
Get Building Live State get_building_live_state
Get a read handle for a building's CURRENT seating — who sits at which desk, as it stands right now. Use this to answer questions and explore options; it leaves nothing behind for anyone to find, appears in no drafts list, and can never be published. Returns the same handle every time for a building, so call it once and reuse the id. When the user commits to a layout, build a real draft instead — this is not it.
| Parameter | Type | Description |
|---|---|---|
buildingId required | string | The building (location) id to read the live state of |