Skip to main content
Portal tools require the Portals add-on. Without it every portal tool returns a forbidden error. Contact support@conduit.ai to enable it.

Overview

A portal is the page someone gets a link to before their stay. What they see there comes from three pieces of workspace configuration, each with its own set of MCP tools:

Verification rules

What has to be collected or signed before check-in. Deposits, agreements, arrival details, questions.

Offers

The things that can be bought or requested. Late checkout, mid-stay clean, airport pickup.

Templates

The page itself: branding, layout blocks, and which offers appear where.
These are independent. You can ship verification rules without ever building a template, and offers exist on their own until a template’s layout references them.

Before you start

All timestamps are epoch milliseconds. All money is whole cents (25000 is $250.00) paired with a lowercase currency code.

Verification rules

A verification rule says “for stays that look like this, collect this before check-in.” Each rule has a step type (what to collect), a config (how to present it), and optional targeting (which stays it applies to).

Step types

The rule that governs everything: one rule per slot

Rules are evaluated in order. For each slot, the first enabled rule whose targeting matches the stay wins, and every later rule for that slot is skipped. This is what makes ordering meaningful. A specific rule has to sit above the general one, or it never fires:
Flip those two and the $500 rule becomes dead configuration: the catch-all claims the slot first, every time. Nothing errors, the specific rule just never applies. Two rules with different step types that share a slot compete the same way. damage_waiver and agreement do not, they sit in different slots and both apply.

Creating a rule

Start with the simplest useful one, arrival details on every stay. Omitting targeting means “every stay”:
requiredFields must be a subset of what you collect. Requiring eta while collectEta is false is rejected. Now a targeted, priced one. This is where the ordering rule starts to matter:
New rules are appended last and enabled by default. Since this one is more specific than a catch-all deposit, move it up (see Reordering).
Agreement. A document to sign, with no charge attached. Pricing an agreement is rejected; use damage_waiver instead.
Damage waiver. A signature that can carry a price. model is flat or per_night; capInCents bounds a per-night total and must be at least amountInCents.
Mandatory fee. Same shape as a deposit but non-refundable, so no releaseDelayDays.
Additional questions. Question ids must be unique within the rule. single_select and multi_select need at least two options; text, long_text, and checkbox take none.
Other guests. Details for everyone else on the booking. Set minGuests, maxGuests, or both. maxGuests caps at 50 and fields caps at 20 entries, each with a unique id.
ID check. Collects an identity document. Takes title and instructions, plus optional requireContactPhoto and autoApprove.

Targeting

Every criterion you set is ANDed. An empty or omitted targeting matches every stay. exclude_returning_guests sits alongside targeting and drops the rule for anyone who has stayed before. Useful for ID checks you only want to run once per person.

Reordering

reorder_verification_rules takes every rule in the workspace, in the order you want, each with its expected_updated_at. Partial lists are rejected:
Omitting a rule would silently renumber the ones you left out, which is exactly how a specific rule ends up buried under a catch-all. Send the whole list.

Editing and deleting

patch_verification_rule cannot change step_type. A config you send has to match the type the rule already has, or you get invalid_input. An agreement or damage_waiver rule whose document was uploaded in the app rejects a config for the same reason: edit its PDF in the app. delete_verification_rule is permanent: stays already collecting that step keep what they have gathered, new stays stop being asked.
Priced rules (security_deposit, mandatory_fee, and damage_waiver with a non-zero price) need Stripe connected before they can be enabled, but only once the workspace has an active template. Until then you can build them freely.

Portal offers

An offer is one purchasable or requestable thing. Offers are workspace-level and reusable: create one, reference it from as many template layouts as you like. kind decides the behaviour: purchase takes payment, request asks without charging, ad just links out via link_url.

Flat pricing

Priced by option

When one offer has variants, use by_option pricing. fieldLabel is what the chooser is called, and each option carries its own price:
by_option requires both fieldLabel and options. Omitting either is rejected.

Pricing off the booking total

percent_price charges a percentage of a booking figure instead of a fixed amount:
of accepts totalPrice, grossRent, netRent, subtotal, totalFees, or totalTaxes.

Targeting and availability

Offer targeting takes listingIds, listingGroupIds, inboxTypeIds (with inboxMode), night bounds, vacancy windows (minVacantNightsBeforeArrival, minVacantNightsAfterDeparture), and customAttributeConditions combined with all or any. Early checkin, for instance, only makes sense when the night before is free:
availability bounds when the offer is visible and bookable using visibleFrom, visibleUntil, bookableFrom, and bookableUntil.

Archiving

archive_portal_offer deactivates an offer and removes it from every template layout that referenced it. restore_portal_offer reactivates it but does not put it back into those layouts; re-add the blocks yourself.

Portal templates

A template is the page. It carries branding, a header, an ordered layout, and checkout behaviour.

Status and versioning

Templates are draft, active, or paused (plus archived). A new template starts as draft unless you say otherwise. Templates use expected_version, an integer, rather than a timestamp.
Editing a template does not change portals already sent. The config is snapshotted onto each portal when it is created, so someone mid-checkout keeps the page they started on. The same is true of guides and the brand kit.

Layout blocks

layout is an ordered array. Every block needs a unique id you choose. trustStrip items are secure_checkout, free_cancellation, verified_business, and powered_by_stripe. Every offerId and offerIds entry has to be a real offer, so create offers first.

Building one

Colours are six-character hex, with or without the leading #.

The rest of the config

listing_data toggles property facts onto the page (address, wifi, parking, doorCode, checkInInstructions, houseRules, and friends), each an independent boolean under fields with an enabled master switch. after_payment sets what happens post-purchase (mode of stay or full_screen, plus message and show_receipt). confirmation_message controls the follow-up message. smart_lock enables lock integration.

Duplicating, archiving, deleting

duplicate_portal_template copies to a new draft, which is the safe way to revise an active portal. archive_portal_template and restore_portal_template are reversible. delete_portal_template is not: it requires confirm_name to match the template’s name exactly, only works when the template has no recorded sales, and deletes the portals created from it.

End to end

Building a working portal with a deposit on long stays:
1

Create the offers

create_portal_offer for each thing being sold. Keep the returned id values.
2

Create the verification rules

create_verification_rule for the deposit and the arrival details. Both land at the end of the list, enabled.
3

Fix the order

reorder_verification_rules with every rule, specific above general, so the long-stay deposit sits above any catch-all.
4

Build the template

create_portal_template with a layout referencing the offer IDs from step 1.
5

Activate

patch_portal_template with status: "active" and the template’s current expected_version.
A prompt that drives the whole thing:

Tool reference

get_portal, update_portal, list_portal_offers, get_customer_portals, and get_checkin_status are runtime tools that serve a portal to a specific person, rather than configuration tools. They read and write one live portal, not the workspace setup described here.

Errors

Read the current value back before retrying a conflict. Both locks are checked against the stored record, so a blind retry with the same value fails the same way.