HTML interfaces
MCP clients: read references/mcp.md. CLI users: read references/cli.md.
An HTML interface is a page whose single element is a customCode element holding one HTML document. The element can declare data sources (pipes or tables) that the HTML reads by source id. Writing the HTML publishes it: there is no draft.
When to use
- "Build a page that shows our open requests", "make a dashboard page from this pipe."
- "Change the layout / copy / colors of my HTML interface."
- "Add the table X as a data source to my page."
- "List my HTML interfaces", "show me the HTML of page Y."
If the user asks for a "page" and it is unclear whether they mean a standalone HTML page or a page inside their main portal, ask before doing anything.
When not to use: the main portal, its grid-layout pages and elements, and sub-portals live in pipefy-portal-setup (list_portals and get_portal cover those, and get_interface_html refuses them). Pipes, fields, and cards themselves live in pipefy-pipes-and-cards. Never change an HTML interface with the portal page or element tools: they accept its customCode element, but they replace its metadata and sources without the checks the tools here make.
Prerequisites
- HTML interfaces enabled on the organization. Pipefy enables them per organization; organization admins cannot turn them on. If a call fails because the feature is not enabled, stop and tell the user to ask Pipefy to enable HTML interfaces for their organization. No tool can enable them.
- Organization id.
list_interface_reposaccepts only the numeric organization id;create_html_interfaceandlist_interfacesaccept the numeric id or the uuid. Users usually give the uuid or the name, so look up the numeric id withlist_organizations(itsid) and use it everywhere. - Admin access to the pipes and tables the page shows.
list_interface_reposlists only published pipes and tables the caller administers.
Operations
| Operation | Read-only | Purpose |
|---|---|---|
get_interface_html_guidelines | Yes | The rules the HTML must follow, the data-pfy-* vocabulary for binding data, and the filter format; with include_design_system, also a design-system catalog. |
list_interface_repos | Yes | Pipes and tables that can be a data source. |
get_interface_repo_fields | Yes | A pipe's or table's custom fields, to pick a source's fieldKeys. |
list_interfaces | Yes | The organization's HTML interfaces, with their visibility. |
get_interface_html | Yes | One interface's pages, elements, data sources, and live HTML. |
create_html_interface | No | Create an interface and write its first page, with optional sources. |
set_interface_sources | No | Replace an element's data sources. |
write_interface_html | No | Replace and publish an element's HTML. |
Rules that hold in every flow
- Read the guidelines first. Call
get_interface_html_guidelinesbefore writing any HTML, and follow it. This skill does not repeat the rules: the guidelines are their single source, and they change with the platform. By default it leaves outdesignSystem, a component catalog several times the size of the rules. Ask for it withinclude_design_systemonly when styling the page. - Bind data exactly as
dataPfyVocabularysays. It says what eachdata-pfy-*attribute does, for most which element it goes on, and the value it takes. The validator only checks that an attribute is allowed, so it accepts a binding in the wrong place. - Every write is live.
create_html_interface,write_interface_html, andset_interface_sourceschange what visitors see at once. A specific request from the user ("rename the heading to X") is approval for that change. Before anything beyond it, such as rewriting markup they did not mention or removing a source they still use, show the user the change and get their approval. - Keep the live page reading only declared sources. HTML that reads a source id the element does not declare renders blank, with no error. Add a source before writing the HTML that reads it. Remove or rename a source only after the live HTML no longer reads it.
- Fix every violation before writing again. A rejected write stores nothing and lists every violation. Fix all of them in one pass, then write again. If the HTML came from the user, show them the violations and a version that follows the rules, and publish only once they agree.
Steps — create a page
-
Read the rules, and the design-system catalog if you will style the page:
textget_interface_html_guidelines get_interface_html_guidelines include_design_system=true -
Check the name is free. If an interface with that name exists, ask the user whether to change it (see the next section) or create another.
textlist_interfaces organization_uuid=<org_id> search_term=<name> -
Pick the data (skip for a page without live data). List candidate repos, then the fields of each one you use:
textlist_interface_repos organization_id=<org_id> repo_name=<name_fragment> get_interface_repo_fields repo_uuid=<repo_uuid>A source is
{"id", "name", "repoId", "fieldKeys", "filters"}.repoIdis the repo'suuid.fieldKeysholds 1 to 30 keys: a custom field'suuid(not itsid), or a built-in key such astitle(the guidelines list built-in keys underfilters.fixedColumnNames).filtersis optional and follows the guidelines' filter format.idandnameare unique within the element; the HTML reads the source byid. An element holds at most 10 sources. -
Write the HTML against the guidelines, binding each source by its
idwith thedataPfyVocabularyattributes. -
Create the interface with its sources and HTML in one call:
textcreate_html_interface organization_uuid=<org_id> name="Open requests" html=<document> data_sources=[{"id": "requests", "name": "Open requests", "repoId": <repo_uuid>, "fieldKeys": ["title", <field_uuid>]}]The result carries the new interface's
uuidand its page and element ids. Tell the user the interface's name anduuid.
Steps — change an existing page
-
Find it and read it:
textlist_interfaces organization_uuid=<org_id> search_term=<name_fragment> get_interface_html interface_uuid=<interface_uuid>Use the element whose
typeiscustomCodeon the page the user means. If the interface has more than one page orcustomCodeelement, list them and ask which one. Takepage_idfrompages[].uuid,element_idfrompages[].elements[].uuid, the current sources from the element'sdataSources, and note itshtmlDocument.contentHash. -
Add sources if the new HTML needs more data. For a field the page does not read yet, find its uuid with
get_interface_repo_fieldson the source'srepoId. Send the complete list, the current sources plus the new ones: it replaces the element's sources, and[]removes them all. Sources read withget_interface_htmlcan be sent back as read. Keep theidof every source you keep.textset_interface_sources interface_uuid=<interface_uuid> page_id=<page_id> element_id=<element_id> data_sources=[...] -
Write the new HTML, starting from the document you read. Right before writing, read the interface again: if
contentHashchanged, someone else edited the page, so start again from the new document and show the user the change.textwrite_interface_html page_id=<page_id> element_id=<element_id> content=<document> -
Remove or rename sources the new HTML no longer reads, if the user wants them gone, with one more
set_interface_sourcescall that sends the complete list.
Success criteria
- The create or write returns success, with no violations.
get_interface_htmlshows the new document and the expecteddataSourceson the element.- Every source id the HTML reads is in the element's
dataSources.
Failure modes
- The create succeeded but the write failed. The interface exists with a blank page, and the error gives its
uuid,page_id, andelement_id. Resolve the error (fix the HTML for a rejection), then callwrite_interface_htmlwith those ids. Do not callcreate_html_interfaceagain: it would create a second, orphaned interface. - The create returned no page to write into. The interface may exist anyway, and the error gives its
uuid. Check it withget_interface_htmlbefore doing anything else, and never retry the create blindly. - The create failed and the error has no
uuid. The interface may exist anyway. Search for it withlist_interfacesby name before any retry, and create again only if no new interface with that name appears. - The HTML was rejected on a write. Nothing was stored. Fix every listed violation against the guidelines and write again.
- A write failed without listing violations. Read the page with
get_interface_htmlto see what is stored, then write again once. If it fails again, stop and tell the user. - The page renders blank sections. The HTML reads a source id that the element does not declare, or one that was renamed or removed. Compare the HTML with
dataSourcesfromget_interface_html, then fix the sources or the HTML. - A duplicate interface exists. Show the user both, with names and uuids. With their approval, delete the extra one with
delete_portal, which takes the interface's uuid asportal_uuidand asks for confirmation. - A repo is missing from
list_interface_repos. It is unpublished, or the caller is not its admin. An empty list can also mean the organization id is wrong: the listing does not report an unknown organization or interface. get_interface_htmlrefuses the interface. It is a grid-layout portal or sub-portal; usepipefy-portal-setup.- Unsure what a source update stored.
set_interface_sourcesreturns the sources as sent, not as stored. Read them back withget_interface_html.
See also
pipefy-portal-setup: the main portal, its grid-layout pages and elements, and sub-portals.pipefy-pipes-and-cards: create the pipes and fields a page reads.


